import type { CampusId, CampusInfo } from '@/shared/types/app';
import type { ScopeTier } from '@/business/scope/selectors';
import type { ActiveTenant } from '@/shared/types/scope';
import type {
  AttendanceScopeMode,
  CampusAttendanceCombinedStats,
  CampusAttendanceChildStats,
  CampusAttendanceEntryDraft,
  CampusAttendanceConfigViewModel,
  CombinedAttendanceHistoryRow,
  CampusAttendanceOverallStats,
  CampusAttendancePrintInput,
  CampusAttendanceScopeModel,
  CampusAttendanceStats,
  CampusAttendanceSummaryViewModel,
  StaffAttendanceDailySummaryViewModel,
} from '@/business/campus-attendance/types';
import type {
  StaffAttendanceRecordViewModel,
  StaffAttendanceSummaryViewModel,
} from '@/business/staff-attendance/types';

export function getWeekStart(date: Date): string {
  const weekStart = new Date(date);
  const day = weekStart.getDay();
  const diff = weekStart.getDate() - day + (day === 0 ? -6 : 1);
  weekStart.setDate(diff);
  return weekStart.toISOString().split('T')[0];
}

export function getWeekEnd(date: Date): string {
  const weekEnd = new Date(date);
  const day = weekEnd.getDay();
  const diff = weekEnd.getDate() - day + (day === 0 ? 0 : 5);
  weekEnd.setDate(diff);
  return weekEnd.toISOString().split('T')[0];
}

export function getToday(): string {
  return new Date().toISOString().split('T')[0];
}

export function formatAttendanceDate(date: string): string {
  const displayDate = new Date(`${date}T12:00:00`);
  return displayDate.toLocaleDateString('en-US', {
    weekday: 'short',
    month: 'short',
    day: 'numeric',
    year: 'numeric',
  });
}

export function parseAttendanceCount(value: string): number | null {
  const parsed = Number.parseInt(value, 10);
  return Number.isNaN(parsed) ? null : parsed;
}

export function buildAttendanceEntryInput(draft: CampusAttendanceEntryDraft, campusId: CampusId) {
  const totalEnrolled = parseAttendanceCount(draft.enrolled);
  const totalPresent = parseAttendanceCount(draft.present);
  const totalAbsent = parseAttendanceCount(draft.absent);
  const totalTardy = parseAttendanceCount(draft.tardy) ?? 0;

  if (totalEnrolled === null || totalPresent === null || totalAbsent === null || totalEnrolled <= 0) {
    return null;
  }

  return {
    campusId,
    date: draft.date,
    totalEnrolled,
    totalPresent,
    totalAbsent,
    totalTardy,
    notes: draft.notes.trim() || null,
  };
}

export function getDraftAttendancePercentage(draft: CampusAttendanceEntryDraft): number | null {
  const totalEnrolled = parseAttendanceCount(draft.enrolled);
  const totalPresent = parseAttendanceCount(draft.present);

  if (totalEnrolled === null || totalPresent === null || totalEnrolled <= 0) {
    return null;
  }

  return (totalPresent / totalEnrolled) * 100;
}

export function getTodayData(
  summaries: readonly CampusAttendanceSummaryViewModel[],
  campusId: CampusId,
  today: string,
): readonly CampusAttendanceSummaryViewModel[] {
  return summaries.filter((record) => record.campus_id === campusId && record.date === today);
}

export function getWeekData(
  summaries: readonly CampusAttendanceSummaryViewModel[],
  campusId: CampusId,
  weekStart: string,
  weekEnd: string,
): readonly CampusAttendanceSummaryViewModel[] {
  return summaries.filter(
    (record) => record.campus_id === campusId && record.date >= weekStart && record.date <= weekEnd,
  );
}

export function getWeeklyAverage(
  summaries: readonly CampusAttendanceSummaryViewModel[],
  campusId: CampusId,
  weekStart: string,
  weekEnd: string,
): number | null {
  const weekData = getWeekData(summaries, campusId, weekStart, weekEnd);

  if (weekData.length === 0) {
    return null;
  }

  const avg = weekData.reduce((sum, record) => sum + record.attendance_percentage, 0) / weekData.length;
  return Number(avg.toFixed(2));
}

export function getTodayPercentage(
  summaries: readonly CampusAttendanceSummaryViewModel[],
  campusId: CampusId,
  today: string,
): number | null {
  const todayData = getTodayData(summaries, campusId, today);
  return todayData[0]?.attendance_percentage ?? null;
}

export function buildCampusAttendanceStats(
  campuses: readonly CampusInfo[],
  configs: readonly CampusAttendanceConfigViewModel[],
  summaries: readonly CampusAttendanceSummaryViewModel[],
  today: string,
  weekStart: string,
  weekEnd: string,
  staffRecords: readonly StaffAttendanceRecordViewModel[] = [],
): readonly CampusAttendanceStats[] {
  const staffDailySummaries = buildStaffAttendanceDailySummaries(staffRecords);

  return campuses.map((campus) => {
    const todayPct = getTodayPercentage(summaries, campus.id, today);
    const weekAvg = getWeeklyAverage(summaries, campus.id, weekStart, weekEnd);
    const config = configs.find((item) => item.campus_id === campus.id) ?? null;
    const recentData = summaries.filter((record) => record.campus_id === campus.id).slice(0, 10);
    const todayRecord = getTodayData(summaries, campus.id, today)[0] ?? null;
    const recentStaffData = staffDailySummaries
      .filter((record) => isStaffSummaryForCampus(record, campus))
      .slice(0, 10);
    const todayStaffRecord = recentStaffData.find((record) => record.date === today) ?? null;

    return {
      ...campus,
      todayPct,
      weekAvg,
      config,
      recentData,
      todayRecord,
      recentStaffData,
      todayStaffRecord,
    };
  });
}

export function buildOverallAttendanceStats(
  summaries: readonly CampusAttendanceSummaryViewModel[],
  today: string,
  weekStart: string,
  weekEnd: string,
): CampusAttendanceOverallStats {
  const todayRecords = summaries.filter((record) => record.date === today);
  const todayEnrolled = todayRecords.reduce((sum, record) => sum + record.total_enrolled, 0);
  const todayPresent = todayRecords.reduce((sum, record) => sum + record.total_present, 0);
  const todayPct = todayEnrolled > 0 ? Number(((todayPresent / todayEnrolled) * 100).toFixed(2)) : null;
  const weekRecords = summaries.filter((record) => record.date >= weekStart && record.date <= weekEnd);
  const weekDays = Array.from(new Set(weekRecords.map((record) => record.date)));
  const weekPct = weekDays.length > 0
    ? Number((weekDays.reduce((sum, day) => {
      const dayRecords = weekRecords.filter((record) => record.date === day);
      const dayEnrolled = dayRecords.reduce((daySum, record) => daySum + record.total_enrolled, 0);
      const dayPresent = dayRecords.reduce((daySum, record) => daySum + record.total_present, 0);
      return sum + (dayEnrolled > 0 ? (dayPresent / dayEnrolled) * 100 : 0);
    }, 0) / weekDays.length).toFixed(2))
    : null;

  return {
    todayRecords,
    todayEnrolled,
    todayPresent,
    todayPct,
    weekRecords,
    weekPct,
  };
}

function getAttendanceScopeMode(tier: ScopeTier): AttendanceScopeMode {
  if (tier === 'organization') {
    return 'organization';
  }
  if (tier === 'school') {
    return 'school';
  }
  return 'campus';
}

export function buildCampusAttendanceScopeModel(
  tier: ScopeTier,
  effectiveTenant: ActiveTenant | null,
  campusId: CampusId | null,
  campusName: string,
): CampusAttendanceScopeModel {
  const mode = getAttendanceScopeMode(tier);
  const tenantName = effectiveTenant?.name ?? null;
  const displayName = tenantName ?? campusName;

  if (mode === 'organization') {
    return {
      tier,
      mode,
      title: 'Organization Attendance',
      description: `${displayName} attendance across all schools, campuses, and organization office staff`,
      aggregateHelper: 'Across organization scope',
      reportLabel: 'Organization',
      emptyHistoryMessage: 'No attendance data recorded yet for this organization.',
      campusId: null,
      tenantName,
    };
  }

  if (mode === 'school') {
    return {
      tier,
      mode,
      title: 'School Attendance',
      description: `${displayName} attendance across all campuses and school staff`,
      aggregateHelper: 'Across school scope',
      reportLabel: 'School',
      emptyHistoryMessage: 'No attendance data recorded yet for this school.',
      campusId: null,
      tenantName,
    };
  }

  if (tier === 'class') {
    return {
      tier,
      mode,
      title: 'Classroom Attendance',
      description: `${displayName} attendance overview`,
      aggregateHelper: 'Current classroom',
      reportLabel: 'Classroom',
      emptyHistoryMessage: 'No attendance data recorded yet for this classroom.',
      campusId,
      tenantName,
    };
  }

  return {
    tier,
    mode,
    title: 'Campus Attendance',
    description: `${displayName} attendance overview`,
    aggregateHelper: 'Current campus',
    reportLabel: 'Campus',
    emptyHistoryMessage: 'No attendance data recorded yet for this campus.',
    campusId,
    tenantName,
  };
}

function percentageFromCounts(present: number, total: number): number | null {
  return total > 0 ? Number(((present / total) * 100).toFixed(2)) : null;
}

function percentageFromScopedChildren(
  childStats: readonly CampusAttendanceChildStats[] | undefined,
  getPercentage: (child: CampusAttendanceChildStats) => number | null,
): number | null {
  if (!childStats || childStats.length === 0) {
    return null;
  }

  const total = childStats.length;
  const presentEquivalent = childStats.reduce((sum, child) => sum + ((getPercentage(child) ?? 0) / 100), 0);
  return percentageFromCounts(presentEquivalent, total);
}

function staffAttendancePercentage(present: number, late: number, total: number): number {
  return percentageFromCounts(present + late, total) ?? 0;
}

function staffDailySummaryId(campusId: CampusId | null, date: string): string {
  return `staff:${campusId ?? 'office'}:${date}`;
}

function isStaffSummaryForCampus(
  record: StaffAttendanceDailySummaryViewModel,
  campus: CampusInfo,
): boolean {
  return record.campus_id === campus.id || record.campus_id === campus.tenantId;
}

export function buildStaffAttendanceDailySummaries(
  records: readonly StaffAttendanceRecordViewModel[],
): readonly StaffAttendanceDailySummaryViewModel[] {
  const grouped = new Map<string, StaffAttendanceRecordViewModel[]>();

  for (const record of records) {
    const key = staffDailySummaryId(record.campusId ?? null, record.date);
    const group = grouped.get(key);

    if (group) {
      group.push(record);
    } else {
      grouped.set(key, [record]);
    }
  }

  return Array.from(grouped.values())
    .map((group) => {
      const firstRecord = group[0];
      const campusId = firstRecord?.campusId ?? null;
      const date = firstRecord?.date ?? '';
      const totalPresent = group.filter((record) => record.status === 'present').length;
      const totalLate = group.filter((record) => record.status === 'late').length;
      const totalAbsent = group.filter((record) => record.status === 'absent').length;
      const notes = group
        .map((record) => record.note)
        .filter((note): note is string => Boolean(note))
        .join('; ');

      return {
        id: staffDailySummaryId(campusId, date),
        campus_id: campusId,
        date,
        total_staff: group.length,
        total_present: totalPresent + totalLate,
        total_absent: totalAbsent,
        total_late: totalLate,
        attendance_percentage: staffAttendancePercentage(totalPresent, totalLate, group.length),
        notes: notes || null,
      };
    })
    .sort((left, right) => right.date.localeCompare(left.date) || left.id.localeCompare(right.id));
}

export function buildCombinedAttendanceStats(
  overallStats: CampusAttendanceOverallStats,
  staffSummary: StaffAttendanceSummaryViewModel | null,
  childStats?: readonly CampusAttendanceChildStats[],
): CampusAttendanceCombinedStats {
  const scopedChildren = childStats ?? [];
  const recordedStaffTotal = staffSummary
    ? staffSummary.present + staffSummary.late + staffSummary.absent
    : 0;
  const staffTotal = staffSummary ? Math.max(staffSummary.staffCount, recordedStaffTotal) : 0;
  const staffPresent = staffSummary ? staffSummary.present + staffSummary.late : 0;
  const scopedStudentTodayPct = percentageFromScopedChildren(scopedChildren, (child) => child.todayPct);
  const scopedStudentWeekPct = percentageFromScopedChildren(scopedChildren, (child) => child.weekAvg);
  const hasScopedChildren = scopedChildren.length > 0;
  const studentTodayPct = scopedStudentTodayPct ?? overallStats.todayPct;
  const studentWeekPct = scopedStudentWeekPct ?? overallStats.weekPct;
  const studentTotal = hasScopedChildren ? scopedChildren.length : overallStats.todayEnrolled;
  const studentPresent = hasScopedChildren
    ? scopedChildren.reduce((sum, child) => sum + ((child.todayPct ?? 0) / 100), 0)
    : overallStats.todayPresent;

  return {
    studentTodayPct,
    studentWeekPct,
    staffTodayPct: percentageFromCounts(staffPresent, staffTotal),
    combinedTodayPct: percentageFromCounts(studentPresent + staffPresent, studentTotal + staffTotal),
  };
}

function notesFromParts(...parts: readonly (string | null | undefined)[]): string | null {
  const notes = parts.filter((part): part is string => Boolean(part));
  return notes.length > 0 ? notes.join('; ') : null;
}

function combinedAttendancePercentage(
  studentRecord: CampusAttendanceSummaryViewModel | null,
  staffRecord: StaffAttendanceDailySummaryViewModel | null,
): number | null {
  const studentTotal = studentRecord?.total_enrolled ?? 0;
  const staffTotal = staffRecord?.total_staff ?? 0;
  const studentPresent = studentRecord?.total_present ?? 0;
  const staffPresent = staffRecord?.total_present ?? 0;

  return percentageFromCounts(studentPresent + staffPresent, studentTotal + staffTotal);
}

export function buildCombinedAttendanceHistoryRows(
  studentRecords: readonly CampusAttendanceSummaryViewModel[],
  staffRecords: readonly StaffAttendanceDailySummaryViewModel[],
): readonly CombinedAttendanceHistoryRow[] {
  const dates = Array.from(new Set([
    ...studentRecords.map((record) => record.date),
    ...staffRecords.map((record) => record.date),
  ])).sort((left, right) => right.localeCompare(left));

  return dates.flatMap((date) => {
    const studentRecord = studentRecords.find((record) => record.date === date) ?? null;
    const staffRecord = staffRecords.find((record) => record.date === date) ?? null;
    const studentTotal = studentRecord?.total_enrolled ?? 0;
    const staffTotal = staffRecord?.total_staff ?? 0;
    const studentPresent = studentRecord?.total_present ?? 0;
    const staffPresent = staffRecord?.total_present ?? 0;
    const studentAbsent = studentRecord?.total_absent ?? 0;
    const staffAbsent = staffRecord?.total_absent ?? 0;
    const studentLate = studentRecord?.total_tardy ?? 0;
    const staffLate = staffRecord?.total_late ?? 0;

    return [
      {
        id: `${date}:students`,
        date,
        group: 'students',
        label: 'Students',
        total: studentTotal,
        present: studentPresent,
        absent: studentAbsent,
        late: studentLate,
        attendancePercentage: studentRecord?.attendance_percentage ?? null,
        notes: studentRecord?.notes ?? null,
      },
      {
        id: `${date}:staff`,
        date,
        group: 'staff',
        label: 'Staff',
        total: staffTotal,
        present: staffPresent,
        absent: staffAbsent,
        late: staffLate,
        attendancePercentage: staffRecord?.attendance_percentage ?? null,
        notes: staffRecord?.notes ?? null,
      },
      {
        id: `${date}:total`,
        date,
        group: 'total',
        label: 'Total',
        total: studentTotal + staffTotal,
        present: studentPresent + staffPresent,
        absent: studentAbsent + staffAbsent,
        late: studentLate + staffLate,
        attendancePercentage: combinedAttendancePercentage(studentRecord, staffRecord),
        notes: notesFromParts(studentRecord?.notes, staffRecord?.notes),
      },
    ] satisfies readonly CombinedAttendanceHistoryRow[];
  });
}

export function buildPrintAttendanceStats(input: CampusAttendancePrintInput) {
  const printEnrolled = input.printTodayRecords.reduce((sum, record) => sum + record.total_enrolled, 0);
  const printPresent = input.printTodayRecords.reduce((sum, record) => sum + record.total_present, 0);
  const todayPct = printEnrolled > 0 ? Number(((printPresent / printEnrolled) * 100).toFixed(2)) : null;
  const staffTotal = input.staffSummary
    ? Math.max(input.staffSummary.staffCount, input.staffSummary.present + input.staffSummary.late + input.staffSummary.absent)
    : 0;
  const staffPresent = input.staffSummary
    ? input.staffSummary.present + input.staffSummary.late
    : 0;
  const staffPct = percentageFromCounts(staffPresent, staffTotal);
  const combinedPct = percentageFromCounts(printPresent + staffPresent, printEnrolled + staffTotal);
  const weekDays = Array.from(new Set(input.printWeekRecords.map((record) => record.date)));
  const weekPct = weekDays.length > 0
    ? Number((weekDays.reduce((sum, day) => {
      const dayRecords = input.printWeekRecords.filter((record) => record.date === day);
      const dayEnrolled = dayRecords.reduce((daySum, record) => daySum + record.total_enrolled, 0);
      const dayPresent = dayRecords.reduce((daySum, record) => daySum + record.total_present, 0);
      return sum + (dayEnrolled > 0 ? (dayPresent / dayEnrolled) * 100 : 0);
    }, 0) / weekDays.length).toFixed(2))
    : null;

  return {
    todayPct,
    staffPct,
    combinedPct,
    weekPct,
  };
}
