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 const listCompanies = asyncHandler(async (req: Request, res: Response) => {
  const { search, status } = req.query;
  const where: any = {};
  if (search) {
    const q = String(search);
    where.OR = [
      { name: { contains: q } },
      { crNo: { contains: q } },
      { unifiedNo: { contains: q } },
    ];
  }
  if (status && String(status) !== "all") where.isActive = String(status) === "active";

  const companies = await prisma.company.findMany({
    where,
    include: { _count: { select: { employees: true, documents: true } } },
    orderBy: { createdAt: "desc" },
  });

  const data = await Promise.all(
    companies.map(async (c) => {
      const expiryDate = c.crExpiry;
      const priority = expiryDate ? summarisePriority(await computePriorityForDate(expiryDate)) : null;
      return { ...c, expiryDate, priority };
    })
  );
  res.json({ success: true, data });
});

export const getCompany = asyncHandler(async (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const company = await prisma.company.findUnique({
    where: { id },
    include: {
      files: { include: { uploader: { select: { id: true, fullName: true } } }, orderBy: { createdAt: "desc" } },
      employees: { orderBy: { createdAt: "desc" } },
      documents: {
        include: {
          documentType: true,
          employee: { select: { id: true, fullName: true } },
          files: { select: { id: true, attachmentName: true, fileName: true, mimeType: true, size: true } },
        },
        orderBy: { expiryDate: "asc" },
      },
    },
  });
  if (!company) throw new ApiError(404, "الشركة غير موجودة");

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

  const expiryItems: {
    type: string;
    entityName: string;
    title: string;
    expiryDate: Date;
    priority: ReturnType<typeof summarisePriority>;
  }[] = [];

  const companyExpiry = company.crExpiry;
  if (companyExpiry) {
    expiryItems.push({
      type: "company",
      entityName: company.name,
      title: "السجل التجاري",
      expiryDate: companyExpiry,
      priority: summarisePriority(await computePriorityForDate(companyExpiry)),
    });
  }

  const employeesWithPriority = await Promise.all(
    company.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,
    }))
  );

  const documentsWithPriority = await Promise.all(
    company.documents.map(async (d) => ({ ...d, priority: summarisePriority(await computePriorityForDate(d.expiryDate)) }))
  );

  for (const e of employeesWithPriority) {
    if (e.iqamaExpiry) { expiryItems.push({ type: "employee", entityName: e.fullName, title: "إقامة", expiryDate: e.iqamaExpiry, priority: e.iqamaPriority! }); }
    if (e.passportExpiry) expiryItems.push({ type: "employee", entityName: e.fullName, title: "جواز سفر", expiryDate: e.passportExpiry, priority: e.passportPriority! });
    if (e.insuranceExpiry) expiryItems.push({ type: "employee", entityName: e.fullName, title: "تأمين طبي", expiryDate: e.insuranceExpiry, priority: e.insurancePriority! });
  }
  for (const d of documentsWithPriority) {
    expiryItems.push({
      type: "document",
      entityName: d.employee?.fullName ?? company.name,
      title: `${d.documentType?.nameAr ?? ""} — ${d.title}`,
      expiryDate: d.expiryDate,
      priority: d.priority,
    });
  }

  const stats = {
    totalEmployees: company.employees.length,
    totalDocuments: company.documents.length,
    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: {
      ...company,
      priority: companyExpiry ? summarisePriority(await computePriorityForDate(companyExpiry)) : null,
      employees: employeesWithPriority,
      documents: documentsWithPriority,
      expiryItems: expiryItems.sort((a, b) => {
        const order = { expired: 0, critical: 1, high: 2, medium: 3, low: 4 };
        const sa = order[a.priority.status as keyof typeof order];
        const sb = order[b.priority.status as keyof typeof order];
        if (sa !== sb) return sa - sb;
        return a.expiryDate.getTime() - b.expiryDate.getTime();
      }),
      stats,
    },
  });
});

export const createCompany = asyncHandler(async (req: Request, res: Response) => {
  const { employees, documents, ...companyData } = req.body as any;

  const result = await prisma.$transaction(async (tx) => {
    const created = await tx.company.create({ data: companyData });

    const createdEmployees: { id: number; fullName: string }[] = [];
    if (Array.isArray(employees) && employees.length) {
      for (const emp of employees) {
        const e = await tx.employee.create({ data: { ...emp, companyId: created.id } });
        createdEmployees.push({ id: e.id, fullName: e.fullName });
      }
    }

    const createdDocuments: { id: number; title: string }[] = [];
    if (Array.isArray(documents) && documents.length) {
      for (const doc of documents) {
        const { employeeId, employeeRef, ...rest } = doc;
        let linkedEmployeeId: number | null = null;
        if (employeeRef != null) {
          const target = createdEmployees[Number(employeeRef)];
          if (!target) throw new ApiError(400, "موظف مضاف غير موجود في قائمة العمالة");
          linkedEmployeeId = target.id;
        } else if (employeeId) {
          const emp = await tx.employee.findUnique({ where: { id: employeeId } });
          if (!emp || emp.companyId !== created.id)
            throw new ApiError(400, "الموظف المحدد لا يتبع هذه الشركة");
          linkedEmployeeId = employeeId;
        }
        const priority = await computePriorityForDate(rest.expiryDate);
        const d = await tx.document.create({
          data: { ...rest, companyId: created.id, employeeId: linkedEmployeeId, priorityScore: priority.priorityScore },
        });
        createdDocuments.push({ id: d.id, title: d.title });
      }
    }

    return { created, createdEmployees, createdDocuments };
  });

  await audit(req, "create", "company", result.created.id, null, {
    ...companyData,
    employees: result.createdEmployees.length,
    documents: result.createdDocuments.length,
  });
  res.status(201).json({
    success: true,
    data: {
      ...result.created,
      employees: result.createdEmployees,
      documents: result.createdDocuments,
    },
  });
});

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

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

  const counts = await prisma.company.findUnique({
    where: { id },
    select: { _count: { select: { employees: true, documents: true } } },
  });
  const empCount = counts?._count.employees ?? 0;
  const docCount = counts?._count.documents ?? 0;
  if (empCount > 0 || docCount > 0) {
    throw new ApiError(
      400,
      `لا يمكن حذف الشركة لوجود ${empCount} موظف و ${docCount} مستند مرتبط بها. احذف أو انقل البيانات المرتبطة أولاً أو عطّل الشركة بدلاً من الحذف.`
    );
  }

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

export const companyExpiries = asyncHandler(async (req: Request, res: Response) => {
  const { companyId } = req.query;
  const companies = await prisma.company.findMany({
    where: companyId ? { id: Number(companyId) } : { crExpiry: { not: null } },
    select: {
      id: true, name: true, crNo: true, unifiedNo: true, crExpiry: true,
      businessActivity: true, isActive: true,
    },
  });
  const withPriority = await Promise.all(
    companies.map(async (c) => {
      const cr = c.crExpiry ? await computePriorityForDate(c.crExpiry) : null;
      return { ...c, crPriority: cr };
    })
  );
  res.json({ success: true, data: withPriority });
});

export const uploadCompanyFile = asyncHandler(async (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const company = await prisma.company.findUnique({ where: { id } });
  if (!company) 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: {
      companyId: id,
      attachmentName,
      filePath: file.filename,
      fileName: decodeFileName(file.originalname),
      mimeType: file.mimetype,
      size: file.size,
      uploadedBy: req.user!.id,
    },
  });
  await audit(req, "upload", "company", id, null, { attachmentName, fileName: decodeFileName(file.originalname) });
  res.status(201).json({ success: true, data: saved });
});