import type { Response } from "express";
import { AttendanceRepository } from "../repositories/AttendanceRepository.js";
import { AttendanceStatus } from "../generated/prisma/client.js";
import { generateUnifiedAttendanceExcel, type AttendanceReportType, type AttendanceExportDataPayload } from "../utils/excel/attendanceExportExcel.js";
import { AttendanceExportPdf } from "../utils/pdf/attendanceExportPdf.js";

export interface UnifiedExportQueryParams {
  reportType: AttendanceReportType;
  format: "pdf" | "excel";
  date?: string;
  fromDate?: string;
  toDate?: string;
  month?: number;
  year?: number;
  employeeId?: string;
}

export class AttendanceService {
  constructor(private readonly attendanceRepository = new AttendanceRepository()) {}

  async getDailyAttendance(dateStr: string, search?: string) {
    return this.attendanceRepository.getDailyAttendance(dateStr, search);
  }

  async saveBulkAttendance(
    dateStr: string,
    records: Array<{
      employeeId: string | number;
      status: AttendanceStatus;
      remarks?: string | null;
    }>,
    createdBy?: bigint
  ) {
    return this.attendanceRepository.saveBulkAttendance(dateStr, records, createdBy);
  }

  async getSummary(dateStr: string) {
    return this.attendanceRepository.getSummary(dateStr);
  }

  async getMonthlyAttendance(year: number, month: number, search?: string) {
    return this.attendanceRepository.getMonthlyAttendance(year, month, search);
  }

  async getEmployeeSummary(employeeId: bigint, year?: number, month?: number) {
    return this.attendanceRepository.getEmployeeSummary(employeeId, year, month);
  }

  async getReportData(
    startDate?: string,
    endDate?: string,
    employeeId?: bigint,
    status?: AttendanceStatus
  ) {
    return this.attendanceRepository.getReportData(startDate, endDate, employeeId, status);
  }

  async exportAttendanceReport(params: UnifiedExportQueryParams, res: Response) {
    const { reportType, format, date, fromDate, toDate, month, year, employeeId } = params;
    const ext = format === "excel" ? "xlsx" : "pdf";

    const validEmpId = employeeId && employeeId !== "ALL" && employeeId !== "" ? String(employeeId) : undefined;
    const empIdBigInt = validEmpId ? BigInt(validEmpId) : undefined;

    let title = "Attendance Report";
    let subtitle = "";
    let filename = `Attendance_Report_${Date.now()}.${ext}`;
    let records: any[] = [];
    let summaryData: any = undefined;
    let daysInMonth: number | undefined = undefined;

    const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

    switch (reportType) {
      case "DAILY": {
        const targetDate: string = date || (new Date().toISOString().split("T")[0] as string);
        title = "Daily Attendance Report";
        subtitle = `Date: ${targetDate}`;
        filename = `Attendance_Daily_${targetDate}.${ext}`;
        records = await this.attendanceRepository.getDailyAttendance(targetDate, undefined);

        if (validEmpId) {
          records = records.filter((r) => String(r.employeeId) === validEmpId);
          if (records.length > 0) {
            subtitle += ` | Employee: ${records[0].name}`;
          }
        }
        break;
      }

      case "EMPLOYEE": {
        if (!validEmpId) throw new Error("Employee ID is required for Employee-wise Attendance Report");
        const empSummary = await this.attendanceRepository.getEmployeeSummary(empIdBigInt!, year, month);
        if (!empSummary) throw new Error("Employee not found");

        const empName = empSummary.employee.name.replace(/[^a-zA-Z0-9]/g, "_");
        title = `${empSummary.employee.name} - Attendance Report`;
        subtitle = `Code: ${empSummary.employee.employeeCode || "-"}`;

        const startDateFilter = fromDate || date;
        const endDateFilter = toDate || date;

        if (startDateFilter && endDateFilter) {
          filename = `${empName}_Attendance_${startDateFilter}_to_${endDateFilter}.${ext}`;
          subtitle += ` | Range: ${startDateFilter} to ${endDateFilter}`;
          records = empSummary.recentLogs.filter((log) => Boolean(log.date) && (log.date as string) >= startDateFilter && (log.date as string) <= endDateFilter);
        } else {
          filename = `${empName}_Attendance_Report.${ext}`;
          records = empSummary.recentLogs;
        }
        summaryData = empSummary.stats;
        break;
      }

      case "MONTHLY_SUMMARY": {
        const y = year || new Date().getFullYear();
        const m = month || new Date().getMonth() + 1;
        const monthName = monthNames[m - 1];
        title = "Monthly Attendance Summary";
        subtitle = `Month: ${monthName} ${y}`;
        filename = `Attendance_Monthly_Summary_${monthName}_${y}.${ext}`;

        const monthlyResult = await this.attendanceRepository.getMonthlyAttendance(y, m);
        records = monthlyResult.employees;
        if (validEmpId) {
          records = records.filter((e) => String(e.employeeId) === validEmpId);
        }
        daysInMonth = monthlyResult.daysInMonth;
        break;
      }

      case "REGISTER": {
        const y = year || new Date().getFullYear();
        const m = month || new Date().getMonth() + 1;
        const monthName = monthNames[m - 1];
        title = "Attendance Register";
        subtitle = `Month: ${monthName} ${y}`;
        filename = `Attendance_Register_${monthName}_${y}.${ext}`;

        const monthlyResult = await this.attendanceRepository.getMonthlyAttendance(y, m);
        records = monthlyResult.employees;
        if (validEmpId) {
          records = records.filter((e) => String(e.employeeId) === validEmpId);
        }
        daysInMonth = monthlyResult.daysInMonth;
        break;
      }

      case "ABSENT": {
        const startDateFilter = fromDate || date;
        const endDateFilter = toDate || date;
        title = "Absent Employees Report";
        subtitle = `Range: ${startDateFilter || "-"} to ${endDateFilter || "-"}`;
        filename = `Absent_Employees_${startDateFilter || "all"}_to_${endDateFilter || "all"}.${ext}`;
        records = await this.attendanceRepository.getReportData(startDateFilter, endDateFilter, empIdBigInt, AttendanceStatus.ABSENT);
        break;
      }

      case "HALF_DAY": {
        const startDateFilter = fromDate || date;
        const endDateFilter = toDate || date;
        title = "Half Day Report";
        subtitle = `Range: ${startDateFilter || "-"} to ${endDateFilter || "-"}`;
        filename = `Half_Day_Report_${startDateFilter || "all"}_to_${endDateFilter || "all"}.${ext}`;
        records = await this.attendanceRepository.getReportData(startDateFilter, endDateFilter, empIdBigInt, AttendanceStatus.HALF_DAY);
        break;
      }

      case "ATTENDANCE_SUMMARY": {
        const y = year || new Date().getFullYear();
        const m = month || new Date().getMonth() + 1;
        const monthName = monthNames[m - 1];
        title = "Overall Attendance Summary";
        subtitle = `Month: ${monthName} ${y}`;
        filename = `Attendance_Summary_${monthName}_${y}.${ext}`;

        const monthlyResult = await this.attendanceRepository.getMonthlyAttendance(y, m);
        records = monthlyResult.employees;
        if (validEmpId) {
          records = records.filter((e) => String(e.employeeId) === validEmpId);
        }
        daysInMonth = monthlyResult.daysInMonth;
        break;
      }
    }

    const payload: AttendanceExportDataPayload = {
      reportType,
      title,
      subtitle,
      records,
      summary: summaryData,
      daysInMonth,
    };

    if (format === "excel") {
      const workbook = await generateUnifiedAttendanceExcel(payload);
      res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
      res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
      res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
      await workbook.xlsx.write(res);
      res.end();
    } else {
      await AttendanceExportPdf.generate(payload, res, filename);
    }
  }
}
