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

export { upload };

export const listDocumentTypes = asyncHandler(async (_req: Request, res: Response) => {
  const types = await prisma.documentType.findMany({ orderBy: { id: "asc" } });
  res.json({ success: true, data: types });
});

export const listDocuments = asyncHandler(async (req: Request, res: Response) => {
  const { search, type, status, priority, companyId, employeeId, days, fromDate, toDate, sortBy, sortDir, page, pageSize } = req.query;
  const where: any = {};
  if (search) {
    const q = String(search);
    where.OR = [
      { title: { contains: q } },
      { referenceNo: { contains: q } },
      { issuingAuthority: { contains: q } },
    ];
  }
  if (type) where.documentTypeId = Number(type);
  if (status && String(status) !== "all") where.status = String(status);
  if (companyId) where.companyId = Number(companyId);
  if (employeeId) where.employeeId = Number(employeeId);

  const thresholds = await getThresholds();
  if (priority && String(priority) !== "all") {
    const today = new Date();
    const inDays = (n: number) => new Date(today.getTime() + n * 86400000);
    switch (String(priority)) {
      case "expired": where.expiryDate = { lt: today }; break;
      case "critical": where.expiryDate = { gte: today, lte: inDays(thresholds.critical) }; break;
      case "high": where.expiryDate = { gt: inDays(thresholds.critical), lte: inDays(thresholds.high) }; break;
      case "medium": where.expiryDate = { gt: inDays(thresholds.high), lte: inDays(thresholds.medium) }; break;
      case "low": where.expiryDate = { gt: inDays(thresholds.medium) }; break;
    }
  }
  if (days) {
    const limit = new Date(Date.now() + Number(days) * 86400000);
    where.expiryDate = { gte: new Date(), lte: limit };
  }
  if (fromDate || toDate) {
    where.expiryDate = where.expiryDate ?? {};
    if (fromDate) where.expiryDate.gte = new Date(String(fromDate));
    if (toDate) where.expiryDate.lte = new Date(String(toDate));
  }

  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 dir = String(sortDir) === "desc" ? "desc" : "asc";
  const orderBy: any = {};
  const allowedSorts: Record<string, string> = { title: "title", expiryDate: "expiryDate", createdAt: "createdAt" };
  if (sortBy && allowedSorts[String(sortBy)]) orderBy[String(sortBy)] = dir;
  else orderBy.expiryDate = "asc";

  const [documents, total] = await Promise.all([
    prisma.document.findMany({
      where,
      include: {
        company: { select: { id: true, name: true } },
        employee: { select: { id: true, fullName: true, iqamaNo: true } },
        documentType: true,
        files: { select: { id: true, attachmentName: true, fileName: true, mimeType: true, size: true } },
      },
      orderBy,
      skip,
      take: size,
    }),
    prisma.document.count({ where }),
  ]);

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

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

async function getThresholds() {
  const settings = await prisma.setting.findMany({
    where: { key: { in: ["priority_critical_days", "priority_high_days", "priority_medium_days"] } },
  });
  const get = (k: string, d: number) => Number(settings.find((s) => s.key === k)?.value ?? d);
  return { critical: get("priority_critical_days", 7), high: get("priority_high_days", 30), medium: get("priority_medium_days", 60) };
}

export const getDocument = asyncHandler(async (req: Request, res: Response) => {
  const document = await prisma.document.findUnique({
    where: { id: Number(req.params.id) },
    include: {
      company: true,
      employee: { include: { company: true } },
      documentType: true,
      files: { include: { uploader: { select: { id: true, fullName: true } } }, orderBy: { createdAt: "desc" } },
    },
  });
  if (!document) throw new ApiError(404, "المستند غير موجود");
  const priority = summarisePriority(await computePriorityForDate(document.expiryDate));
  res.json({ success: true, data: { ...document, priority } });
});

export const createDocument = asyncHandler(async (req: Request, res: Response) => {
  const { companyId, employeeId, documentTypeId, title, referenceNo, issuingAuthority, issueDate, expiryDate, notes } = req.body;
  const company = await prisma.company.findUnique({ where: { id: companyId } });
  if (!company) throw new ApiError(404, "الشركة غير موجودة");
  if (employeeId) {
    const emp = await prisma.employee.findUnique({ where: { id: employeeId } });
    if (!emp || emp.companyId !== companyId) throw new ApiError(400, "الموظف لا يتبع هذه الشركة");
  }
  const type = await prisma.documentType.findUnique({ where: { id: documentTypeId } });
  if (!type) throw new ApiError(404, "نوع المستند غير موجود");

  const priority = await computePriorityForDate(expiryDate);
  const document = await prisma.document.create({
    data: {
      companyId, employeeId: employeeId ?? null, documentTypeId,
      title, referenceNo: referenceNo ?? null, issuingAuthority: issuingAuthority ?? null,
      issueDate: issueDate ?? null, expiryDate,
      priorityScore: priority.priorityScore,
    },
  });
  await audit(req, "create", "document", document.id, null, { title, expiryDate });
  res.status(201).json({ success: true, data: document });
});

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

  const data = { ...req.body };
  if (data.companyId) {
    const company = await prisma.company.findUnique({ where: { id: data.companyId } });
    if (!company) throw new ApiError(404, "الشركة غير موجودة");
  }
  if (data.employeeId) {
    const companyId = data.companyId ?? existing.companyId;
    const emp = await prisma.employee.findUnique({ where: { id: data.employeeId } });
    if (!emp || emp.companyId !== companyId) throw new ApiError(400, "الموظف لا يتبع هذه الشركة");
  }
  if (data.expiryDate) {
    const priority = await computePriorityForDate(data.expiryDate);
    data.priorityScore = priority.priorityScore;
    data.isAlerted = false; // إعادة تفعيل التنبيه عند تغيير تاريخ الانتهاء
  }

  const document = await prisma.document.update({ where: { id }, data });
  await audit(req, "update", "document", id, existing, document);
  res.json({ success: true, data: document });
});

export const deleteDocument = asyncHandler(async (req: Request, res: Response) => {
  const id = Number(req.params.id);
  const existing = await prisma.document.findUnique({ where: { id }, include: { files: true } });
  if (!existing) throw new ApiError(404, "المستند غير موجود");
  for (const f of existing.files) deleteStoredFile(f.filePath);
  await prisma.document.delete({ where: { id } });
  await audit(req, "delete", "document", id, existing, null);
  res.json({ success: true });
});

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

export const downloadFile = asyncHandler(async (req: Request, res: Response) => {
  const file = await prisma.file.findUnique({ where: { id: Number(req.params.fileId) } });
  if (!file) throw new ApiError(404, "الملف غير موجود");
  const filePath = resolveFilePath(file.filePath); // آمن ضد path traversal
  if (!fs.existsSync(filePath)) throw new ApiError(404, "الملف محذوف من الخادم");
  res.download(filePath, file.fileName);
});

export const deleteFile = asyncHandler(async (req: Request, res: Response) => {
  const file = await prisma.file.findUnique({ where: { id: Number(req.params.fileId) } });
  if (!file) throw new ApiError(404, "الملف غير موجود");
  deleteStoredFile(file.filePath);
  await prisma.file.delete({ where: { id: file.id } });
  const entityType = file.documentId ? "document" : file.employeeId ? "employee" : "company";
  const entityId = file.documentId ?? file.employeeId ?? file.companyId;
  await audit(req, "delete_file", entityType, entityId, null, { attachmentName: file.attachmentName, fileName: file.fileName });
  res.json({ success: true });
});

export const prioritySummary = asyncHandler(async (req: Request, res: Response) => {
  const { companyId } = req.query;
  const companyFilter = companyId ? { companyId: Number(companyId) } : {};
  const thresholds = await getThresholds();
  const today = new Date();
  const inDays = (n: number) => new Date(today.getTime() + n * 86400000);
  const [expired, critical, high, medium, low] = await Promise.all([
    prisma.document.count({ where: { ...companyFilter, expiryDate: { lt: today } } }),
    prisma.document.count({ where: { ...companyFilter, expiryDate: { gte: today, lte: inDays(thresholds.critical) } } }),
    prisma.document.count({ where: { ...companyFilter, expiryDate: { gt: inDays(thresholds.critical), lte: inDays(thresholds.high) } } }),
    prisma.document.count({ where: { ...companyFilter, expiryDate: { gt: inDays(thresholds.high), lte: inDays(thresholds.medium) } } }),
    prisma.document.count({ where: { ...companyFilter, expiryDate: { gt: inDays(thresholds.medium) } } }),
  ]);
  res.json({
    success: true,
    data: [
      { key: "expired", label: PRIORITY_LABELS.expired, count: expired },
      { key: "critical", label: PRIORITY_LABELS.critical, count: critical },
      { key: "high", label: PRIORITY_LABELS.high, count: high },
      { key: "medium", label: PRIORITY_LABELS.medium, count: medium },
      { key: "low", label: PRIORITY_LABELS.low, count: low },
    ],
  });
});