import type { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { AttendanceService } from "../services/AttendanceService.js";
import { AttendancePdf } from "../utils/pdf/AttendancePdf.js";
import { generateAttendanceExcel } from "../utils/excel/attendanceExcel.js";

const bulkSaveSchema = z.object({
  date: z.string().min(1, "Date is required"),
  records: z.array(
    z.object({
      employeeId: z.string().or(z.number()),
      status: z.enum(["PRESENT", "ABSENT", "HALF_DAY"]),
      remarks: z.string().nullable().optional(),
    })
  ),
});

const exportQuerySchema = z.object({
  reportType: z.enum([
    "DAILY",
    "EMPLOYEE",
    "MONTHLY_SUMMARY",
    "REGISTER",
    "ABSENT",
    "HALF_DAY",
    "ATTENDANCE_SUMMARY",
  ]).default("DAILY"),
  format: z.enum(["pdf", "excel"]).default("pdf"),
  date: z.string().optional(),
  fromDate: z.string().optional(),
  toDate: z.string().optional(),
  month: z.coerce.number().min(1).max(12).optional(),
  year: z.coerce.number().min(2000).max(2100).optional(),
  employeeId: z.string().optional(),
});

export class AttendanceController {
  constructor(private readonly attendanceService = new AttendanceService()) {}

  getDaily = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const dateStr = (req.query.date as string) || (new Date().toISOString().split("T")[0] as string);
      const search = req.query.search ? String(req.query.search) : undefined;

      const items = await this.attendanceService.getDailyAttendance(dateStr, search);
      const summary = await this.attendanceService.getSummary(dateStr);

      res.status(200).json({
        message: "Daily attendance fetched successfully",
        result: {
          items,
          summary,
        },
      });
    } catch (error) {
      next(error);
    }
  };

  saveBulk = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const validated = bulkSaveSchema.parse(req.body);
      const createdBy = req.user?.id ? BigInt(req.user.id) : undefined;
      const summary = await this.attendanceService.saveBulkAttendance(
        validated.date,
        validated.records as any,
        createdBy
      );
      res.status(200).json({
        success: true,
        message: "Attendance saved successfully",
        result: summary,
      });
    } catch (error: any) {
      if (error instanceof z.ZodError) {
        const errors: Record<string, string> = {};
        error.issues.forEach((err) => {
          if (err.path[0]) errors[err.path[0].toString()] = err.message;
        });
        res.status(422).json({ message: "Validation failed", errors });
        return;
      }
      res.status(400).json({ message: error.message || "Failed to save attendance" });
    }
  };

  getSummary = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const dateStr = (req.query.date as string) || (new Date().toISOString().split("T")[0] as string);
      const summary = await this.attendanceService.getSummary(dateStr);
      res.status(200).json({ success: true, result: summary });
    } catch (error) {
      next(error);
    }
  };

  getMonthly = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const now = new Date();
      const year = req.query.year ? Number(req.query.year) : now.getFullYear();
      const month = req.query.month ? Number(req.query.month) : now.getMonth() + 1;
      const search = req.query.search ? String(req.query.search) : undefined;

      const monthlyData = await this.attendanceService.getMonthlyAttendance(year, month, search);
      res.status(200).json({ success: true, result: monthlyData });
    } catch (error) {
      next(error);
    }
  };

  getEmployeeSummary = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { employeeId } = req.params;
      const idStr = Array.isArray(employeeId) ? employeeId[0] : employeeId;
      if (!idStr) {
        res.status(400).json({ message: "Employee ID is required" });
        return;
      }
      const year = req.query.year ? Number(req.query.year) : undefined;
      const month = req.query.month ? Number(req.query.month) : undefined;

      const summary = await this.attendanceService.getEmployeeSummary(BigInt(idStr), year, month);
      if (!summary) {
        res.status(404).json({ message: "Employee not found" });
        return;
      }
      res.status(200).json({ success: true, result: summary });
    } catch (error) {
      next(error);
    }
  };

  getReport = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { startDate, endDate, employeeId, status } = req.query;

      const empIdBigInt = employeeId ? BigInt(String(employeeId)) : undefined;

      const records = await this.attendanceService.getReportData(
        startDate ? String(startDate) : undefined,
        endDate ? String(endDate) : undefined,
        empIdBigInt,
        status ? (String(status) as any) : undefined
      );

      res.status(200).json({ success: true, result: records });
    } catch (error) {
      next(error);
    }
  };

  exportReport = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const validated = exportQuerySchema.parse(req.query);
      await this.attendanceService.exportAttendanceReport(validated as any, res);
    } catch (error: any) {
      if (error instanceof z.ZodError) {
        res.status(400).json({ message: "Invalid export query parameters", errors: error.issues });
        return;
      }
      next(error);
    }
  };

  exportPdf = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { startDate, endDate, employeeId, status } = req.query;
      const empIdBigInt = employeeId ? BigInt(String(employeeId)) : undefined;

      const records = await this.attendanceService.getReportData(
        startDate ? String(startDate) : undefined,
        endDate ? String(endDate) : undefined,
        empIdBigInt,
        status ? (String(status) as any) : undefined
      );

      const filters: { startDate?: string; endDate?: string; status?: string } = {};
      if (startDate) filters.startDate = String(startDate);
      if (endDate) filters.endDate = String(endDate);
      if (status) filters.status = String(status);

      await AttendancePdf.generate(records, res, filters);
    } catch (error) {
      next(error);
    }
  };

  exportExcel = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { startDate, endDate, employeeId, status } = req.query;
      const empIdBigInt = employeeId ? BigInt(String(employeeId)) : undefined;

      const records = await this.attendanceService.getReportData(
        startDate ? String(startDate) : undefined,
        endDate ? String(endDate) : undefined,
        empIdBigInt,
        status ? (String(status) as any) : undefined
      );

      const workbook = await generateAttendanceExcel(records);

      res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
      res.setHeader("Content-Disposition", `attachment; filename="attendance-report-${Date.now()}.xlsx"`);
      res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

      await workbook.xlsx.write(res);
      res.end();
    } catch (error) {
      next(error);
    }
  };
}
