import {
  Student,
  TrackerPart,
  MarkData,
  CriterionState
} from '../models/ProgressTrackerModels';
import { SortConfig } from '../services/ProgressTrackerStateManager';

/**
 * Utility functions for the Progress Tracker
 */

/**
 * Sort students based on the provided configuration
 */
export function sortStudents(
  students: Student[],
  markData: { [studentId: number]: MarkData } = {},
  sortConfig: SortConfig = { sortBy: null, sortDirection: 'asc' }
): Student[] {
  const { sortBy, sortDirection } = sortConfig;

  if (!sortBy) {
    return [...students]; // No sorting, just return a copy
  }

  return [...students].sort((a, b) => {
    let comparison = 0;

    if (sortBy === 'score') {
      // Sort by score
      const scoreA = markData[a.id]?.score ?? -1;
      const scoreB = markData[b.id]?.score ?? -1;
      comparison = (scoreA as number) - (scoreB as number);
    } else if (sortBy === 'grade') {
      // Sort by grade
      const gradeA = markData[a.id]?.grade || '';
      const gradeB = markData[b.id]?.grade || '';
      comparison = gradeA.localeCompare(gradeB);
    } else {
      // Sort by student property (name, id, etc.)
      const valA = a[sortBy] as string | number;
      const valB = b[sortBy] as string | number;

      if (typeof valA === 'string' && typeof valB === 'string') {
        comparison = valA.localeCompare(valB);
      } else {
        comparison = (valA as number) - (valB as number);
      }
    }

    // Adjust for direction
    return sortDirection === 'asc' ? comparison : -comparison;
  });
}

export const ucFirst = str => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();


/**
 * Filter parts based on parent and category
 */
export function filterPartsByParentAndCategory(
  parts: TrackerPart[],
  parentId: number | null,
  category: string
): TrackerPart[] {
  return parts.filter(part =>
    part.category.ref === category &&
    part.parent === parentId
  );
}

/**
 * Check if a part is a "Show All" option
 */
export function isShowAllOption(part: TrackerPart | null): boolean {
  return part !== null && part.isShowAll === true;
}

/**
 * Get the next state for a criterion in the cycle
 */
export function getNextCriterionState(currentState: CriterionState | null): CriterionState | null {
  return CriterionState.getNextState(currentState);
}

/**
 * Format the display text for a criterion state
 */
export function formatCriterionStateText(state: CriterionState | null): string {
  if (!state) return '';

  if (state.attained) {
    return `A${state.attemptNumber}`;
  } else if (state.referred) {
    return `R${state.attemptNumber}`;
  } else if (state.failed) {
    return 'F';
  }

  return '';
}

/**
 * Get the color for a criterion state
 */
export function getCriterionStateColor(state: CriterionState | null): string {
  if (!state) return '';

  if (state.attained) {
    return '#4caf50'; // Green for attained
  } else if (state.referred) {
    return '#ff9800'; // Orange for referred
  } else if (state.failed) {
    return '#f44336'; // Red for failed
  }

  return '';
}

/**
 * Calculate the completion percentage for a student based on criteria
 */
export function calculateCompletionPercentage(markData: MarkData, totalCriteria: number): number {
  if (!markData.criteria || totalCriteria === 0) return 0;

  const completedCriteria = Object.values(markData.criteria).filter(state => state !== null).length;
  return Math.round((completedCriteria / totalCriteria) * 100);
}

/**
 * Check if a specific tracker part has been filtered and is visible
 * Enhanced to handle filter state and "Show All" options
 */
export function isPartVisible(partId: number, visiblePartIds: Set<number>, hierarchySelections?: { [category: string]: TrackerPart | null } | null, partsById?: { [id: number]: TrackerPart } | null): boolean {
  // First check direct visibility
  if (visiblePartIds.has(partId)) {
    return true;
  }

  // If we have hierarchy selections and parts map, check for "Show All" options
  if (hierarchySelections && partsById && partsById[partId]) {
    const part = partsById[partId];
    const categorySelection = hierarchySelections[part.category.ref];

    // If "Show All" is selected for this part's category, the part should be visible
    if (categorySelection && isShowAllOption(categorySelection)) {
      return true;
    }

    // Check if any parent category has "Show All" selected
    if (part.parent && partsById[part.parent]) {
      const parentPart = partsById[part.parent];
      const parentCategorySelection = hierarchySelections[parentPart.category.ref];
      if (parentCategorySelection && isShowAllOption(parentCategorySelection)) {
        // For parent with "Show All", children should be visible if they belong
        // to the currently selected category
        const selectedCategory = Object.keys(hierarchySelections).find(
          cat => hierarchySelections[cat] && !isShowAllOption(hierarchySelections[cat])
        );

        return selectedCategory === null || selectedCategory === part.category.ref;
      }
    }
  }

  return false;
}

/**
 * Get all criteria names from the mark data
 */
export function getAllCriteriaNames(markDataByPart: { [studentId: number]: MarkData }): string[] {
  const criteriaNames = new Set<string>();

  Object.values(markDataByPart).forEach(studentMark => {
    if (studentMark.criteria) {
      Object.keys(studentMark.criteria).forEach(name => criteriaNames.add(name));
    }
  });

  return Array.from(criteriaNames).sort();
}

/**
 * Calculate the pass rate for a specific criterion across all students
 */
export function calculateCriterionPassRate(
  criterionName: string,
  markDataByPart: { [studentId: number]: MarkData },
  totalStudents: number
): number {
  if (totalStudents === 0) return 0;

  let passCount = 0;

  Object.values(markDataByPart).forEach(studentMark => {
    const criterionState = studentMark.criteria?.[criterionName];
    if (criterionState?.attained) {
      passCount++;
    }
  });

  return Math.round((passCount / totalStudents) * 100);
}

/**
 * Format a date for display
 */
export function formatDate(date: Date): string {
  return new Intl.DateTimeFormat('en-GB', {
    day: '2-digit',
    month: 'short',
    year: 'numeric'
  }).format(date);
}
