import type { Request, Response } from "express";
import { prisma } from "../../config/prisma.js";
import { ApiError, asyncHandler } from "../../utils/asyncHandler.js";
import { audit } from "../../utils/audit.js";
import { computePriorityForDate, summarisePriority } from "../../utils/priority.js";
import { deleteStoredFile, decodeFileName } from "../../utils/upload.js";

export function buildEmployeeWhere(q: any): any {
  const { search, companyId, status, nationality, priority } = q;
  const where: any = {};
  if (search) {
    const query = String(search);
    where.OR = [
      { fullName: { contains: query } },
      { iqamaNo: { contains: query } },
      { passportNo: { contains: query } },
      { insuranceNo: { contains: query } },
      { phone: { contains: query } },
    ];
  }
  if (companyId) where.companyId = Number(companyId);
  if (status && String(status) !== "all") where.status = String(status);
  if (nationality && String(nationality) !== "all") where.nationality = String(nationality);

  if (priority && String(priority) !== "all") {
    const now = new Date();
    const inDays = (n: number) => new Date(now.getTime() + n * 86400000);
    switch (String(priority)) {
      case "expired": where.iqamaExpiry = { lt: now }; break;
      case "critical": where.iqamaExpiry = { gte: now, lte: inDays(7) }; break;
      case "high": where.iqamaExpiry = { gt: inDays(7), lte: inDays(30) }; break;
      case "medium": where.iqamaExpiry = { gt: inDays(30), lte: inDays(60) }; break;
      case "low": where.iqamaExpiry = { gt: inDays(60) }; break;
    }
  }
  return where;
}

export const listEmployees = asyncHandler(async (req: Request, res: Response) => {
  const { page, pageSize, sortBy, sortDir } = req.query;

  const where = buildEmployeeWhere(req.query);

  const pg = Math.max(1, Number(page) || 1);
  const size = Math.min(200, Math.max(1, Number(pageSize) || 20));
  const skip = (pg - 1) * size;
  const orderBy: any = {};
  const allowedSorts: Record<string, any> = {
    fullName: "fullName",
    company: { company: { name: "asc" } },
    iqamaExpiry: "iqamaExpiry",
    passportExpiry: "passportExpiry",
    createdAt: "createdAt",
  };
  if (sortBy && allowedSorts[String(sortBy)]) {
    const dir = String(sortDir) === "desc" ? "desc" : "asc";
    if (String(sortBy) === "company") orderBy.company = { name: dir };
    else orderBy[String(sortBy)] = dir;
  } else {
    orderBy.createdAt = "desc";
  }

  const [employees, total] = await Promise.all([
    prisma.employee.findMany({
      where,
      include: {
        company: { select: { id: true, name: true } },
        _count: { select: { documents: true } },
      },
      orderBy,
      skip,
      take: size,
    }),
    prisma.employee.count({ where }),
  ]);

  const data = await Promise.all(
    employees.map(async (e) => ({
      ...e,
      iqamaPriority: e.iqamaExpiry ? summarisePriority(await computePriorityForDate(e.iqamaExpiry)) : null,
      passportPriority: e.passportExpiry ? summarisePriority(await computePriorityForDate(e.passportExpiry)) : null,
      insurancePriority: e.insuranceExpiry ? summarisePriority(await computePriorityForDate(e.insuranceExpiry)) : null,
    }))
  );

  res.json({ success: true, data, pagination: { page: pg, pageSize: size, total, pages: Math.ceil(total / size) } });
});

export const getEmployee = asyncHandler(async (req: Request, res: Response) => {
  const employee = await prisma.employee.findUnique({
    where: { id: Number(req.params.id) },
    include: {
      company: true,
      files: { include: { uploader: { select: { id: true, fullName: true } } }, orderBy: { createdAt: "desc" } },
    },
  });
  if (!employee) throw new ApiError(404, "الموظف غير موجود");

  const documents = await prisma.document.findMany({
    where: { employeeId: employee.id },
    include: { documentType: true, company: { select: { id: true, name: true } } },
    orderBy: { expiryDate: "asc" },
  });
  const docsWithPriority = await Promise.all(
    documents.map(async (d) => ({ ...d, priority: summarisePriority(await computePriorityForDate(d.expiryDate)) }))
  );

  const now = new Date();
  const inDays = (n: number) => new Date(now.getTime() + n * 86400000);

  const warnings: string[] = [];
  if (employee.iqamaExpiry) {
    const days = Math.floor((employee.iqamaExpiry.getTime() - now.getTime()) / 86400000);
    if (days < 0) warnings.push("الإقامة منتهية");
    else if (days <= 30) warnings.push(`الإقامة تنتهي خلال ${days} يوم`);
  }
  if (employee.passportExpiry) {
    const days = Math.floor((employee.passportExpiry.getTime() - now.getTime()) / 86400000);
    if (days < 0) warnings.push("الجواز منتهي");
    else if (days <= 30) warnings.push(`الجواز ينتهي خلال ${days} يوم`);
  }
  if (employee.insuranceExpiry) {
    const days = Math.floor((employee.insuranceExpiry.getTime() - now.getTime()) / 86400000);
    if (days < 0) warnings.push("التأمين الطبي منتهي");
    else if (days <= 30) warnings.push(`التأمين الطبي ينتهي خلال ${days} يوم`);
  }

  const expiryItems: any[] = [];
  if (employee.iqamaExpiry) expiryItems.push({ type: "employee", sub: "iqama", title: "إقامة", expiryDate: employee.iqamaExpiry, priority: summarisePriority(await computePriorityForDate(employee.iqamaExpiry)) });
  if (employee.passportExpiry) expiryItems.push({ type: "employee", sub: "passport", title: "جواز سفر", expiryDate: employee.passportExpiry, priority: summarisePriority(await computePriorityForDate(employee.passportExpiry)) });
  if (employee.insuranceExpiry) expiryItems.push({ type: "employee", sub: "insurance", title: "تأمين طبي", expiryDate: employee.insuranceExpiry, priority: summarisePriority(await computePriorityForDate(employee.insuranceExpiry)) });

  const stats = {
    expired: expiryItems.filter((i) => i.priority.status === "expired").length,
    critical7: expiryItems.filter((i) => i.priority.status !== "expired" && i.expiryDate.getTime() <= inDays(7).getTime()).length,
    expiring30: expiryItems.filter((i) => i.priority.status !== "expired" && i.expiryDate.getTime() <= inDays(30).getTime()).length,
    expiring60: expiryItems.filter((i) => i.priority.status !== "expired" && i.expiryDate.getTime() <= inDays(60).getTime()).length,
  };

  res.json({
    success: true,
    data: {
      ...employee,
      iqamaPriority: employee.iqamaExpiry ? summarisePriority(await computePriorityForDate(employee.iqamaExpiry)) : null,
      passportPriority: employee.passportExpiry ? summarisePriority(await computePriorityForDate(employee.passportExpiry)) : null,
      insurancePriority: employee.insuranceExpiry ? summarisePriority(await computePriorityForDate(employee.insuranceExpiry)) : null,
      documents: docsWithPriority,
      warnings,
      stats,
      expiryItems,
    },
  });
});

export const createEmployee = asyncHandler(async (req: Request, res: Response) => {
  const data = req.body;
  const company = await prisma.company.findUnique({ where: { id: data.companyId } });
  if (!company) throw new ApiError(404, "الشركة غير موجودة");
  const employee = await prisma.employee.create({ data });
  await audit(req, "create", "employee", employee.id, null, data);
  res.status(201).json({ success: true, data: employee });
});

export const updateEmployee = asyncHandler(async (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const existing = await prisma.employee.findUnique({ where: { id } });
  if (!existing) throw new ApiError(404, "الموظف غير موجود");

  const data: any = { ...req.body };
  if (data.companyId) {
    const company = await prisma.company.findUnique({ where: { id: data.companyId } });
    if (!company) throw new ApiError(404, "الشركة غير موجودة");
  }
  const employee = await prisma.employee.update({ where: { id }, data });
  await audit(req, "update", "employee", id, existing, employee);
  res.json({ success: true, data: employee });
});

export const deleteEmployee = asyncHandler(async (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const existing = await prisma.employee.findUnique({ where: { id } });
  if (!existing) throw new ApiError(404, "الموظف غير موجود");

  const files = await prisma.file.findMany({ where: { employeeId: id } });
  for (const f of files) deleteStoredFile(f.filePath);
  await prisma.employee.delete({ where: { id } });
  await audit(req, "delete", "employee", id, existing, null);
  res.json({ success: true, deletedFiles: files.length });
});

export const employeeDocuments = asyncHandler(async (req: Request, res: Response) => {
  const employeeId = Number(req.params.id);
  const documents = await prisma.document.findMany({
    where: { employeeId },
    include: { documentType: true },
    orderBy: { expiryDate: "asc" },
  });
  const data = await Promise.all(
    documents.map(async (d) => ({ ...d, priority: await computePriorityForDate(d.expiryDate) }))
  );
  res.json({ success: true, data });
});

export const uploadEmployeeFile = asyncHandler(async (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const employee = await prisma.employee.findUnique({ where: { id } });
  if (!employee) throw new ApiError(404, "الموظف غير موجود");

  const file = (req as any).file;
  if (!file) throw new ApiError(400, "الملف مطلوب");
  const attachmentName = String((req.body?.name as string) || "").trim();
  if (!attachmentName) {
    deleteStoredFile(file.filename);
    throw new ApiError(400, "اسم المرفق مطلوب");
  }

  const saved = await prisma.file.create({
    data: {
      employeeId: id,
      attachmentName,
      filePath: file.filename,
      fileName: decodeFileName(file.originalname),
      mimeType: file.mimetype,
      size: file.size,
      uploadedBy: req.user!.id,
    },
  });
  await audit(req, "upload", "employee", id, null, { attachmentName, fileName: decodeFileName(file.originalname) });
  res.status(201).json({ success: true, data: saved });
});