import PDFDocument from "pdfkit";
import path from "node:path";

import { prisma } from "@/lib/prisma";
import { supportsProcedureFollowup } from "@/services/procedures.service";

const ASSET_ROOT = path.join(process.cwd(), "public", "procedure-assets");
const FONT_REGULAR = "ProcedureRegular";
const FONT_BOLD = "ProcedureBold";
const FONT_ITALIC = "ProcedureItalic";

function assetPath(name: string) {
  return path.join(ASSET_ROOT, name);
}

function registerFonts(doc: PDFKit.PDFDocument) {
  doc.registerFont(FONT_REGULAR, assetPath("NimbusSans-Regular.otf"));
  doc.registerFont(FONT_BOLD, assetPath("NimbusSans-Bold.otf"));
  doc.registerFont(FONT_ITALIC, assetPath("NimbusSans-Italic.otf"));
}

type PdfProcedure = {
  id: number;
  manager_name: string;
  request_type: string;
  other_type: string | null;
  facts_date: Date | null;
  facts_time: string | null;
  observed_by: string | null;
  facts_location: string | null;
  facts_description: string;
  witnesses: string | null;
  written_statements: boolean | null;
  created_at: Date;
  employee: {
    COS: number | null;
    TIT: string | null;
    NSA: string | null;
    PRE: string | null;
  };
  followup: {
    interview_at: Date | null;
    interviewer_name: string | null;
    employee_present: boolean | null;
    accompanied_by: string | null;
    decision_type: string | null;
    other_decision: string | null;
    suspension_days: number | null;
    suspension_start: Date | null;
    suspension_end: Date | null;
    decision_reasons: string | null;
    facts_acknowledged: boolean | null;
    employee_statement: string | null;
    decision_maker_name: string | null;
    hr_validation_sent_at: Date | null;
    notification_sent_at: Date | null;
  } | null;
};

const REQUEST_LABELS: Record<string, string> = {
  DEMANDE_NOUVELLES: "Demande de nouvelles",
  LETTRE_RECADRAGE: "Lettre de recadrage",
  AVERTISSEMENT_DIRECT: "Avertissement direct",
  CONVOCATION_SANCTION: "Convocation entretien préalable à une sanction",
  CONVOCATION_LICENCIEMENT_RUPTURE:
    "Convocation entretien préalable à un licenciement ou rupture anticipée du contrat",
  AUTRE: "Autre",
};

const DECISION_LABELS: Record<string, string> = {
  LETTRE_RECADRAGE: "Lettre de recadrage",
  AVERTISSEMENT: "Avertissement",
  MISE_A_PIED: "Mise à pied",
  MUTATION_DISCIPLINAIRE: "Mutation disciplinaire (changement de poste)",
  RETROGRADATION: "Rétrogradation",
  LICENCIEMENT_CAUSE_REELLE_SERIEUSE:
    "Licenciement (CDI) pour cause réelle et sérieuse",
  LICENCIEMENT_RUPTURE_FAUTE_GRAVE:
    "Licenciement (CDI) ou rupture anticipée (CDD) pour faute grave",
  LICENCIEMENT_RUPTURE_FAUTE_LOURDE:
    "Licenciement (CDI) ou rupture anticipée (CDD) pour faute lourde",
  LICENCIEMENT_RUPTURE_INAPTITUDE:
    "Licenciement ou rupture anticipée pour inaptitude avec impossibilité de reclassement",
  AUTRE: "Autre",
};

function frenchDate(value: Date | null | undefined) {
  return value ? value.toLocaleDateString("fr-FR", { timeZone: "Europe/Paris" }) : "";
}

function frenchDateTime(value: Date | null | undefined) {
  return value
    ? value.toLocaleString("fr-FR", {
        timeZone: "Europe/Paris",
        dateStyle: "short",
        timeStyle: "short",
      })
    : "";
}

function checkbox(doc: PDFKit.PDFDocument, x: number, y: number, checked: boolean) {
  doc.rect(x, y, 10, 10).stroke();
  if (checked) {
    doc.font(FONT_BOLD).fontSize(9).text("X", x + 1.7, y + 0.2, {
      lineBreak: false,
    });
  }
}

function optionLine(
  doc: PDFKit.PDFDocument,
  x: number,
  y: number,
  label: string,
  checked: boolean,
) {
  checkbox(doc, x, y + 2, checked);
  doc.font(FONT_REGULAR).fontSize(9).text(label, x + 17, y, { width: 475 });
}

function sectionBox(
  doc: PDFKit.PDFDocument,
  title: string,
  x: number,
  y: number,
  width: number,
  height: number,
) {
  doc.rect(x, y, width, height).stroke();
  doc.font(FONT_BOLD).fontSize(11).text(title, x + 8, y + 8);
}

function header(
  doc: PDFKit.PDFDocument,
  title: string,
  logoPath?: string,
) {
  if (logoPath) {
    doc.image(logoPath, 20, 8, { width: 160 });
  }
  doc.font(FONT_BOLD).fontSize(22).text(title, logoPath ? 165 : 45, 40, {
    width: logoPath ? 350 : 505,
    align: "center",
  });
  doc.font(FONT_REGULAR).fontSize(7).text("E2E-S2-ENR08", 515, 25, {
    width: 45,
    align: "right",
  });
}

function employeeName(item: PdfProcedure) {
  return [item.employee.NSA, item.employee.PRE].filter(Boolean).join(" ");
}

async function resolveLogoPath(item: PdfProcedure) {
  const fallback = assetPath("logo-envie-2e.jpg");
  if (!item.employee.COS) return fallback;

  try {
    const contract = await prisma.contrats.findFirst({
      where: { Id_Salarie: item.employee.COS },
      orderBy: [{ DAE: "desc" }, { id: "desc" }],
      select: { ETB: true },
    });
    const establishment = contract?.ETB
      ? await prisma.etablissement.findFirst({
          where: { etb: contract.ETB },
          select: { entreprise: true },
        })
      : null;
    const company = `${establishment?.entreprise ?? ""} ${contract?.ETB ?? ""}`
      .normalize("NFD")
      .replace(/[\u0300-\u036f]/g, "")
      .toUpperCase();

    if (company.includes("ENVIE") && !company.includes("ENVIE 2E") && !company.includes("ENVIE2E")) {
      return assetPath("logo-envie-magasins.jpg");
    }
  } catch (error) {
    console.warn("[procedure-pdf] Impossible de déterminer le logo", error);
  }

  return fallback;
}

function drawRequestPage(
  doc: PDFKit.PDFDocument,
  item: PdfProcedure,
  companyLogoPath: string,
) {
  doc.addPage({ size: "A4", margin: 40 });
  header(doc, "Demande de procédure", companyLogoPath);

  doc.font(FONT_REGULAR).fontSize(10);
  doc.text(`Nom du responsable hiérarchique : ${item.manager_name}`, 45, 95);
  doc.text(`Nom et prénom du salarié : ${employeeName(item)}`, 45, 118);

  sectionBox(doc, "Type de demande", 45, 145, 505, 130);
  const requestTypes = Object.entries(REQUEST_LABELS);
  requestTypes.forEach(([value, label], index) => {
    optionLine(doc, 57, 169 + index * 17, label, item.request_type === value);
  });
  if (item.request_type === "AUTRE" && item.other_type) {
    doc.font(FONT_ITALIC).fontSize(8).text(item.other_type, 115, 258, {
      width: 420,
    });
  }

  sectionBox(
    doc,
    "Faits reprochés, motifs précis et exhaustifs",
    45,
    290,
    505,
    405,
  );
  doc.font(FONT_REGULAR).fontSize(9);
  doc.text(
    `Date des faits : ${frenchDate(item.facts_date)}     Heure : ${item.facts_time ?? ""}`,
    57,
    320,
  );
  doc.text(`Personne ayant constaté les faits : ${item.observed_by ?? ""}`, 57, 340);
  doc.text(`Lieu des faits : ${item.facts_location ?? ""}`, 57, 360);
  doc.font(FONT_BOLD).text("Motifs :", 57, 385);
  doc.font(FONT_REGULAR).text(item.facts_description, 57, 402, {
    width: 480,
    height: 190,
    ellipsis: true,
    lineGap: 2,
  });
  doc.font(FONT_BOLD).text("Témoins des faits :", 57, 602);
  doc.font(FONT_REGULAR).text(item.witnesses || "Aucun témoin renseigné", 145, 602, {
    width: 390,
    height: 45,
    ellipsis: true,
  });
  doc.font(FONT_REGULAR).text("Attestations écrites réalisées", 57, 662);
  checkbox(doc, 220, 662, item.written_statements === true);
  doc.text("Oui", 235, 660);
  checkbox(doc, 275, 662, item.written_statements === false);
  doc.text("Non", 290, 660);

  doc.rect(45, 710, 505, 85).stroke();
  doc.moveTo(215, 710).lineTo(215, 795).stroke();
  doc.moveTo(380, 710).lineTo(380, 795).stroke();
  doc.font(FONT_REGULAR).fontSize(8);
  doc.text(`Fait le : ${frenchDate(item.created_at)}`, 53, 720);
  doc.text("Signature du responsable", 53, 742);
  doc.font(FONT_BOLD).fontSize(8).text("Accord Direction", 225, 720, {
    width: 145,
    align: "center",
  });
  checkbox(doc, 245, 747, false);
  doc.font(FONT_REGULAR).text("Oui", 260, 746);
  checkbox(doc, 300, 747, false);
  doc.text("Non", 315, 746);
  doc.text("Le :", 230, 771);
  doc.text("Réservé au service Ressources Humaines", 390, 720, { width: 150 });
  doc.text("Date et heure de l’entretien :", 390, 746, { width: 150 });
  doc.text("Courrier envoyé/remis :", 390, 766);
}

function drawFollowupPage(doc: PDFKit.PDFDocument, item: PdfProcedure) {
  doc.addPage({ size: "A4", margin: 40 });
  header(doc, "Suite de procédure");
  const followup = item.followup;

  doc.font(FONT_REGULAR).fontSize(10);
  doc.text(`Date de l’entretien : ${frenchDateTime(followup?.interview_at)}`, 45, 100);
  doc.text(`Personne qui réalise l’entretien : ${followup?.interviewer_name ?? ""}`, 285, 100);
  doc.text("Salarié présent ?", 45, 125);
  checkbox(doc, 130, 125, followup?.employee_present === true);
  doc.text("Oui", 145, 123);
  checkbox(doc, 180, 125, followup?.employee_present === false);
  doc.text("Non", 195, 123);
  doc.text(`En présence de : ${followup?.accompanied_by ?? ""}`, 285, 125);

  sectionBox(doc, "Décision", 45, 160, 505, 245);
  Object.entries(DECISION_LABELS).forEach(([value, label], index) => {
    optionLine(doc, 57, 185 + index * 20, label, followup?.decision_type === value);
  });
  if (followup?.decision_type === "MISE_A_PIED") {
    doc.font(FONT_ITALIC).fontSize(8).text(
      `${followup.suspension_days ?? ""} jour(s), du ${frenchDate(followup.suspension_start)} au ${frenchDate(followup.suspension_end)}`,
      260,
      225,
      { width: 270 },
    );
  }
  if (followup?.decision_type === "AUTRE" && followup.other_decision) {
    doc.font(FONT_ITALIC).fontSize(8).text(followup.other_decision, 110, 365, {
      width: 420,
    });
  }

  sectionBox(doc, "Justification de la décision", 45, 420, 505, 285);
  doc.font(FONT_BOLD).fontSize(9).text("Motifs de la décision :", 57, 450);
  doc.font(FONT_REGULAR).text(followup?.decision_reasons ?? "", 57, 468, {
    width: 480,
    height: 85,
    ellipsis: true,
  });
  doc.text("Le salarié a reconnu les faits", 57, 565);
  checkbox(doc, 205, 565, followup?.facts_acknowledged === true);
  doc.text("Oui", 220, 563);
  checkbox(doc, 260, 565, followup?.facts_acknowledged === false);
  doc.text("Non", 275, 563);
  doc.font(FONT_BOLD).text("Justification du salarié :", 57, 592);
  doc.font(FONT_REGULAR).text(followup?.employee_statement ?? "", 57, 610, {
    width: 480,
    height: 75,
    ellipsis: true,
  });

  doc.rect(45, 720, 505, 75).stroke();
  doc.moveTo(300, 720).lineTo(300, 795).stroke();
  doc.font(FONT_REGULAR).fontSize(8);
  doc.text(`Fait le : ${frenchDate(followup?.interview_at)}`, 53, 730);
  doc.text(`Décisionnaire : ${followup?.decision_maker_name ?? ""}`, 53, 750);
  doc.text("Signature du décisionnaire", 53, 770);
  doc.text("Réservé au service Ressources Humaines", 310, 730);
  doc.text(`Envoi DRH : ${frenchDateTime(followup?.hr_validation_sent_at)}`, 310, 750);
  doc.text(`Notification : ${frenchDateTime(followup?.notification_sent_at)}`, 310, 770);
}

function drawInstructionsPage(doc: PDFKit.PDFDocument) {
  doc.addPage({ size: "A4", margin: 55 });
  doc.font(FONT_BOLD).fontSize(15).text("Procédure de demande de procédure", {
    underline: true,
  });
  doc.moveDown(1.2);
  const steps = [
    "Le responsable remplit la demande de manière exhaustive : date, heure, lieu, circonstances, actes, paroles et témoins.",
    "Le responsable signe sa demande.",
    "Le service RH recueille des témoignages écrits si les faits sont graves.",
    "Le formulaire est transmis au Directeur, qui accepte ou refuse la procédure.",
    "Le service RH réalise la notification ou la convocation après validation éventuelle de la DRH.",
    "Après l’entretien, le décisionnaire précise et justifie sa décision, relate les dires du salarié et signe.",
    "Le service RH notifie la décision au salarié après validation de la DRH.",
  ];
  doc.font(FONT_REGULAR).fontSize(11);
  steps.forEach((step, index) => {
    doc.text(`${index + 1}.  ${step}`, { indent: 10, paragraphGap: 10, lineGap: 2 });
  });
}

export async function generateProcedurePdf(item: PdfProcedure) {
  const companyLogoPath = await resolveLogoPath(item);
  const doc = new PDFDocument({ autoFirstPage: false, info: {
    Title: `Demande de procédure ${item.id}`,
    Author: "RH Connect",
  } });
  registerFonts(doc);
  const chunks: Buffer[] = [];
  doc.on("data", (chunk: Buffer) => chunks.push(chunk));

  const completed = new Promise<Buffer>((resolve, reject) => {
    doc.on("end", () => resolve(Buffer.concat(chunks)));
    doc.on("error", reject);
  });

  drawRequestPage(doc, item, companyLogoPath);
  if (supportsProcedureFollowup(item.request_type)) {
    drawFollowupPage(doc, item);
  }
  drawInstructionsPage(doc);
  doc.end();
  return completed;
}
