import PizZip from "pizzip";

import type {
  AttestationEmploiPdfData,
  AttestationImpotsPdfData,
  AttestationMutuellePdfData,
  AttestationProlongationMutuellePdfData,
  AttestationRetraitePdfData,
  AutorisationPrefecturePdfData,
} from "@/services/impressions.service";

export type WordDocumentKind =
  | "attestation-impots"
  | "attestation-retraite"
  | "attestation-emploi"
  | "attestation-mutuelle"
  | "attestation-prolongation-mutuelle"
  | "autorisation-prefecture";

type WordDocumentData =
  | AttestationImpotsPdfData
  | AttestationRetraitePdfData
  | AttestationEmploiPdfData
  | AttestationMutuellePdfData
  | AttestationProlongationMutuellePdfData
  | AutorisationPrefecturePdfData;

type ParagraphOptions = {
  align?: "left" | "center" | "right" | "both";
  bold?: boolean;
  border?: boolean;
  size?: number;
  spacingAfter?: number;
  spacingBefore?: number;
};

const MUTUELLE_LEGAL_TEXT = [
  "Décret n° 2015-1883 du 30 décembre 2015 pris pour l'application de l'article 34 de la loi n° 2015-1702 du 21 décembre 2015 de financement de la sécurité sociale pour 2016 :",
  "L'article D. 911-2 du code de la sécurité sociale est remplacé par les dispositions suivantes :",
  "« Art. D. 911-2.-Sans préjudice des dispositions du deuxième alinéa du III de l'article L. 911-7, peuvent se dispenser, à leur initiative, de l'obligation d'adhésion à la couverture en matière de remboursement complémentaire de frais occasionnés par une maladie, une maternité ou un accident mise en place dans leur entreprise :",
  "« 1° Les salariés bénéficiaires d'une couverture complémentaire en application de l'article L. 861-3 ou d'une aide à l'acquisition d'une complémentaire santé en application de l'article L. 863-1. La dispense ne peut jouer que jusqu'à la date à laquelle les salariés cessent de bénéficier de cette couverture ou de cette aide ;",
  "« 2° Les salariés couverts par une assurance individuelle de frais de santé au moment de la mise en place des garanties ou de l'embauche si elle est postérieure. La dispense ne peut jouer que jusqu'à échéance du contrat individuel ;",
  "« 3° Les salariés qui bénéficient, pour les mêmes risques, y compris en tant qu'ayants droit, de prestations servies au titre d'un autre emploi en tant que bénéficiaire de l'un ou l'autre des dispositifs suivants :",
  "« a) Dispositif de garanties remplissant les conditions mentionnées au sixième alinéa de l'article L. 242-1 ;",
  "« b) Dispositif de garanties prévu par le décret n° 2007-1373 du 19 septembre 2007 relatif à la participation de l'Etat et de ses établissements publics au financement de la protection sociale complémentaire de leurs personnels ou par le décret n° 2011-1474 du 8 novembre 2011 relatif à la participation des collectivités territoriales et de leurs établissements publics au financement de la protection sociale complémentaire de leurs agents ;",
  "« c) Contrats d'assurance de groupe issus de la loi n° 94-126 du 11 février 1994 relative à l'initiative et à l'entreprise individuelle ;",
  "« d) Régime local d'assurance maladie du Haut-Rhin, du Bas-Rhin et de la Moselle, en application des articles D. 325-6 et D. 325-7 du code de la sécurité sociale ;",
  "« e) Régime complémentaire d'assurance maladie des industries électriques et gazières en application du décret n° 46-1541 du 22 juin 1946.",
  "« Dans tous les cas, l'employeur doit être en mesure de produire la demande de dispense des salariés concernés. »",
].join("\n");

function escapeXml(value: string) {
  return value
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/\"/g, "&quot;")
    .replace(/'/g, "&apos;");
}

function textRuns(text: string, bold = false, size = 22) {
  return text
    .split("\n")
    .map((line, index) => {
      const breakBefore = index > 0 ? "<w:br/>" : "";
      return `<w:r><w:rPr>${bold ? "<w:b/>" : ""}<w:sz w:val="${size}"/><w:szCs w:val="${size}"/></w:rPr>${breakBefore}<w:t xml:space="preserve">${escapeXml(line)}</w:t></w:r>`;
    })
    .join("");
}

function paragraph(text: string, options: ParagraphOptions = {}) {
  const {
    align = "left",
    bold = false,
    border = false,
    size = 22,
    spacingAfter = 160,
    spacingBefore = 0,
  } = options;
  const borderXml = border
    ? '<w:pBdr><w:top w:val="single" w:sz="6" w:space="2"/><w:left w:val="single" w:sz="6" w:space="2"/><w:bottom w:val="single" w:sz="6" w:space="2"/><w:right w:val="single" w:sz="6" w:space="2"/></w:pBdr>'
    : "";

  return `<w:p><w:pPr><w:jc w:val="${align}"/><w:spacing w:before="${spacingBefore}" w:after="${spacingAfter}" w:line="276" w:lineRule="auto"/>${borderXml}</w:pPr>${textRuns(text, bold, size)}</w:p>`;
}

function labelValue(label: string, value: string) {
  return `<w:p><w:pPr><w:spacing w:after="85"/><w:tabs><w:tab w:val="left" w:pos="2500"/></w:tabs></w:pPr>${textRuns(`${label} `, true, 19)}${textRuns(value, false, 19)}</w:p>`;
}

function formatLongFrenchDate(value: string) {
  const [year, month, day] = value.split("-").map(Number);
  if (!year || !month || !day) return value;

  const formatted = new Intl.DateTimeFormat("fr-FR", {
    timeZone: "Europe/Paris",
    day: "numeric",
    month: "long",
    year: "numeric",
  }).format(new Date(Date.UTC(year, month - 1, day, 12)));
  return day === 1 ? formatted.replace(/^1 /, "1er ") : formatted;
}

function signature(data: {
  lieuDocument: string;
  dateDocument: string;
  signataireNom?: string;
  signataireFonction?: string;
}) {
  return [
    paragraph(
      `Fait à ${data.lieuDocument}, le ${formatLongFrenchDate(data.dateDocument)}`,
      { align: "right", spacingBefore: 420, spacingAfter: 360 },
    ),
    data.signataireNom
      ? paragraph(
          `${data.signataireNom}\n${data.signataireFonction ?? ""}`,
          { align: "right", spacingAfter: 0 },
        )
      : "",
  ].join("");
}

function qualitePoste(poste: string) {
  const firstLetter = poste
    .trim()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .charAt(0)
    .toLowerCase();
  return "aeiouyh".includes(firstLetter) ? `d'${poste}` : `de ${poste}`;
}

function employmentSentence(data: AttestationImpotsPdfData) {
  const start = formatLongFrenchDate(data.dateEmbauche);
  if (data.estToujoursDansEffectifs) {
    return `${data.titreNomPrenom} ${data.domicilie} ${data.adresseLigne} travaille au sein de notre société en ${data.typeContrat} depuis le ${start} en qualité ${qualitePoste(data.poste)} et est toujours dans nos effectifs à ce jour.`;
  }
  const end = data.dateFinContrat
    ? ` au ${formatLongFrenchDate(data.dateFinContrat)}`
    : "";
  return `${data.titreNomPrenom} ${data.domicilie} ${data.adresseLigne} a travaillé au sein de notre société en ${data.typeContrat} du ${start}${end} en qualité ${qualitePoste(data.poste)}.`;
}

function attestationTitle() {
  return paragraph("ATTESTATION", {
    align: "center",
    bold: true,
    border: true,
    size: 30,
    spacingAfter: 760,
  });
}

function renderAttestationImpots(data: AttestationImpotsPdfData) {
  return [
    attestationTitle(),
    paragraph(
      `Je ${data.formuleSoussignataire}, ${data.signataireNom} - ${data.signataireFonction} de la ${data.entreprise}, atteste par la présente que ${employmentSentence(data)}`,
      { align: "both", spacingAfter: 340 },
    ),
    paragraph("Attestation établie pour servir et valoir ce que de droit."),
    signature(data),
  ].join("");
}

function renderAttestationRetraite(data: AttestationRetraitePdfData) {
  return [
    attestationTitle(),
    paragraph(
      `Je ${data.formuleSoussignataire}, ${data.signataireNom} - ${data.signataireFonction} de la ${data.entreprise}, atteste que ${data.titreNomPrenom} ${data.domicilie} ${data.adresseLigne}, employé(e) en ${data.typeContrat} depuis le ${formatLongFrenchDate(data.dateEmbauche)} en qualité ${qualitePoste(data.poste)}, quittera nos effectifs le ${formatLongFrenchDate(data.dateSortiePrevue)}.`,
      { align: "both", spacingAfter: 300 },
    ),
    paragraph(
      `Son départ à la retraite prendra effet le ${formatLongFrenchDate(data.dateRetraite)}.`,
      { align: "both", spacingAfter: 300 },
    ),
    paragraph("Pour faire et valoir ce que de droit."),
    signature(data),
  ].join("");
}

function renderAttestationEmploi(data: AttestationEmploiPdfData) {
  return [
    attestationTitle(),
    paragraph(
      `Je ${data.formuleSoussignataire}, ${data.signataireNom} - ${data.signataireFonction} de la ${data.entreprise}, atteste par la présente que ${data.titreNomPrenom} ${data.domicilie} ${data.adresseLigne}, a effectué les contrats suivants au sein de notre société :`,
      { align: "both", spacingAfter: 300 },
    ),
    paragraph(data.contratsTexte, { spacingBefore: 120, spacingAfter: 360 }),
    paragraph("Pour faire et valoir ce que de droit."),
    signature(data),
  ].join("");
}

function renderAttestationMutuelle(data: AttestationMutuellePdfData) {
  const echeance = data.dateEcheanceMutuelle
    ? ` À l'échéance de ce dernier, le ${formatLongFrenchDate(data.dateEcheanceMutuelle)},`
    : " À la date d'échéance de ce dernier,";
  return [
    paragraph("ATTESTATION", {
      align: "center",
      bold: true,
      border: true,
      size: 30,
      spacingAfter: 260,
    }),
    paragraph(
      `Je ${data.formuleSoussignataire}, ${data.signataireNom} - ${data.signataireFonction} de la ${data.entreprise}, atteste par la présente que ${employmentSentence(data)}`,
      { align: "both", size: 19, spacingAfter: 180 },
    ),
    paragraph(
      `${data.titreNomPrenom} a déjà bénéficié d'une dérogation à la souscription de la mutuelle de l'entreprise puisqu'il disposait déjà d'un contrat à titre personnel.${echeance} il se voit dans l'obligation d'adhérer à notre partenaire prévoyance.`,
      { align: "both", size: 19, spacingAfter: 180 },
    ),
    paragraph(MUTUELLE_LEGAL_TEXT, {
      align: "left",
      size: 15,
      spacingAfter: 150,
    }),
    paragraph("Pour faire et valoir ce que de droit.", { size: 19 }),
    signature(data),
  ].join("");
}

function renderProlongationMutuelle(
  data: AttestationProlongationMutuellePdfData,
) {
  return [
    attestationTitle(),
    paragraph(
      `Je ${data.formuleSoussignataire}, ${data.signataireNom} - ${data.signataireFonction} de la ${data.entreprise}, atteste par la présente que ${data.titreNomPrenom} ${data.domicilie} ${data.adresseLigne} a travaillé du ${formatLongFrenchDate(data.dateEmbauche)} au ${formatLongFrenchDate(data.dateSortiePrevue)} au sein de notre société en ${data.typeContrat}. Il sera repris en contrat à durée déterminée le ${formatLongFrenchDate(data.dateReprise)}.`,
      { align: "both", spacingAfter: 320 },
    ),
    paragraph(
      "Je vous demande donc de bien vouloir prolonger son contrat mutuelle jusqu'à sa sortie définitive.",
      { align: "both", spacingAfter: 420 },
    ),
    paragraph("Pour faire et valoir ce que de droit."),
    signature(data),
  ].join("");
}

function sectionTitle(title: string) {
  return `<w:p><w:pPr><w:shd w:val="clear" w:color="auto" w:fill="D9D9D9"/><w:pBdr><w:top w:val="single" w:sz="4"/><w:left w:val="single" w:sz="4"/><w:bottom w:val="single" w:sz="4"/><w:right w:val="single" w:sz="4"/></w:pBdr><w:spacing w:before="150" w:after="150"/></w:pPr>${textRuns(title, true, 21)}</w:p>`;
}

function renderAutorisationPrefecture(data: AutorisationPrefecturePdfData) {
  return [
    paragraph("PREFECTURE DU NORD", {
      align: "center",
      bold: true,
      size: 24,
      spacingAfter: 260,
    }),
    paragraph(
      "DEMANDE DE VERIFICATION PREALABLE DU TITRE DE SEJOUR ET/OU DE L'AUTORISATION DE TRAVAIL",
      { align: "center", bold: true, size: 22, spacingAfter: 360 },
    ),
    sectionTitle("SOCIETE :"),
    labelValue("Entreprise :", data.entreprise),
    labelValue("Adresse :", data.adresseEntreprise),
    labelValue(
      "Correspondant :",
      `${data.correspondantNom} - ${data.correspondantFonction}`,
    ),
    labelValue("Tél. :", data.telephoneEntreprise),
    labelValue("N° SIRET :", data.siret),
    labelValue("N° URSSAF :", data.urssaf),
    sectionTitle("SALARIE(E) :"),
    labelValue("NOM - Prénom :", data.nomPrenom),
    labelValue("Adresse :", data.adresseSalarie),
    labelValue("Né(e) le :", formatLongFrenchDate(data.dateNaissance)),
    labelValue("Pays de naissance :", data.paysNaissance),
    labelValue("Nationalité :", data.nationalite),
    labelValue("N° Sécurité sociale :", data.numeroSecuriteSociale),
    labelValue("Titre de séjour :", data.titreSejour),
    labelValue("Motif du séjour :", data.motifSejour),
    labelValue("N° carte de séjour :", data.numeroCarteSejour),
    labelValue("Délivrée le :", formatLongFrenchDate(data.dateDelivrance)),
    labelValue("Expire le :", formatLongFrenchDate(data.dateExpiration)),
    sectionTitle("CONTRAT DE TRAVAIL :"),
    labelValue("Début du contrat :", formatLongFrenchDate(data.dateDebutContrat)),
    labelValue("Durée du contrat :", data.dureeContrat),
    labelValue("Type de contrat :", data.typeContrat),
    labelValue("Fonction :", data.poste),
    labelValue("Heures hebdomadaires :", data.heuresHebdomadaires),
    labelValue("Pièce jointe :", "copie du titre produit"),
    signature(data),
  ].join("");
}

function documentBody(document: WordDocumentKind, data: WordDocumentData) {
  switch (document) {
    case "attestation-impots":
      return renderAttestationImpots(data as AttestationImpotsPdfData);
    case "attestation-retraite":
      return renderAttestationRetraite(data as AttestationRetraitePdfData);
    case "attestation-emploi":
      return renderAttestationEmploi(data as AttestationEmploiPdfData);
    case "attestation-mutuelle":
      return renderAttestationMutuelle(data as AttestationMutuellePdfData);
    case "attestation-prolongation-mutuelle":
      return renderProlongationMutuelle(
        data as AttestationProlongationMutuellePdfData,
      );
    case "autorisation-prefecture":
      return renderAutorisationPrefecture(data as AutorisationPrefecturePdfData);
  }
}

export function renderDocumentDocx(
  document: WordDocumentKind,
  data: WordDocumentData,
) {
  const zip = new PizZip();
  const body = documentBody(document, data);

  zip.file(
    "[Content_Types].xml",
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>',
  );
  zip.folder("_rels")?.file(
    ".rels",
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/></Relationships>',
  );
  zip.folder("word")?.file(
    "document.xml",
    `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>${body}<w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="850" w:right="1250" w:bottom="850" w:left="1250" w:header="360" w:footer="360" w:gutter="0"/></w:sectPr></w:body></w:document>`,
  );
  zip.folder("word")?.file(
    "styles.xml",
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:docDefaults><w:rPrDefault><w:rPr><w:rFonts w:ascii="Arial" w:hAnsi="Arial" w:eastAsia="Arial" w:cs="Arial"/><w:sz w:val="22"/><w:szCs w:val="22"/><w:lang w:val="fr-FR"/></w:rPr></w:rPrDefault><w:pPrDefault><w:pPr><w:spacing w:after="160" w:line="276" w:lineRule="auto"/></w:pPr></w:pPrDefault></w:docDefaults><w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style></w:styles>',
  );
  zip.folder("word")?.folder("_rels")?.file(
    "document.xml.rels",
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>',
  );
  zip.folder("docProps")?.file(
    "core.xml",
    `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${escapeXml(document)}</dc:title><dc:creator>RH Connect</dc:creator><cp:lastModifiedBy>RH Connect</cp:lastModifiedBy><dcterms:created xsi:type="dcterms:W3CDTF">${new Date().toISOString()}</dcterms:created></cp:coreProperties>`,
  );
  zip.folder("docProps")?.file(
    "app.xml",
    '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>RH Connect</Application></Properties>',
  );

  return zip.generate({
    type: "nodebuffer",
    compression: "DEFLATE",
    mimeType:
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  }) as Buffer;
}
