import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { createRequire } from "module";

import { PDFDocument } from "pdf-lib";
import { Resvg } from "@resvg/resvg-js";

const require = createRequire(import.meta.url);
const fontkit = require("fontkit");

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const apiRoot = path.resolve(__dirname, "../../..");
const uiPublicFundsDir = path.join(apiRoot, "src/images/funds");
const defaultFontPath = path.join(
  apiRoot,
  "src/images/fonts/Vazirmatn-UI-FD-NL-Regular.ttf",
);

const CERTIFICATE_FIELDS = [
  "workingDate",
  "serialNumber",
  "fundRegisterNO",
  "requestDate",
  "firstName",
  "lastName",
  "fatherName",
  "companyName",
  "birthDayDate",
  "idCardNO",
  "cardPlace",
  "nationalCode",
  "evidenceType",
  "evidenceTypeString",
  "title",
  "count",
  "persianCount",
  "fundTitle",
  "registerNO",
  "registerPlace",
  "registerDate",
];

const pageWidth = 3507;
const pageHeight = 2481;
const CERTIFICATE_DISCLAIMER =
  "این گواهی بنا به درخواست سرمایه گذار محترم صادر گردیده است. جهت دریافت نسخه اصلی و تأییدشده، خواهشمند است با واحد پشتیبانی شرکت تأمین سرمایه امید تماس حاصل فرمایید با سپاس از حسن توجه شما";

const normalizeTextValue = (value, fallback = "") => {
  if (value === undefined || value === null) {
    return fallback;
  }

  return String(value).trim();
};

const normalizeNullableTextValue = (value) => {
  if (value === undefined || value === null || value === "") {
    return null;
  }

  return String(value).trim();
};

const parseNumericValue = (value, fallback = 0) => {
  if (typeof value === "number") {
    return Number.isFinite(value) ? value : fallback;
  }

  if (typeof value === "string") {
    const normalizedValue = value.replaceAll(",", "").trim();
    const parsedValue = Number(normalizedValue);
    return Number.isFinite(parsedValue) ? parsedValue : fallback;
  }

  return fallback;
};

const toPersianDigits = (value) =>
  String(value ?? "").replace(/\d/g, (digit) => "۰۱۲۳۴۵۶۷۸۹"[Number(digit)]);

const formatPersianNumber = (value) =>
  new Intl.NumberFormat("fa-IR").format(parseNumericValue(value, 0));

const formatCertificateDateFromCreatedAt = (value) => {
  if (!value) {
    return "";
  }

  const parsedValue = new Date(value);
  if (Number.isNaN(parsedValue.getTime())) {
    return normalizeTextValue(value);
  }

  return new Intl.DateTimeFormat("fa-IR-u-nu-latn", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).format(parsedValue);
};

const resolveCertificateAssetPaths = (fundRegisterNO) => {
  const normalizedFundSeoRegisterNo = String(fundRegisterNO || "").trim();

  if (!normalizedFundSeoRegisterNo) {
    return null;
  }

  const fundAssetDir = path.join(uiPublicFundsDir, normalizedFundSeoRegisterNo);

  return {
    templatePath: path.join(fundAssetDir, "certificate-template.png"),
    signPath: path.join(fundAssetDir, "certificate-sign.png"),
    sharedSignPath: path.join(uiPublicFundsDir, "sign.png"),
  };
};

const escapeXml = (value) =>
  String(value || "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&apos;");

const appendRtlPeriod = (value) => `${String(value || "").trim()}۔`;

export const normalizeCertificateEvidenceData = (payload = {}) => {
  const normalizedPayload = {};

  for (const field of CERTIFICATE_FIELDS) {
    normalizedPayload[field] = payload[field];
  }

  return {
    workingDate: normalizeTextValue(normalizedPayload.workingDate),
    serialNumber: normalizeTextValue(normalizedPayload.serialNumber),
    fundRegisterNO: normalizeTextValue(normalizedPayload.fundRegisterNO),
    requestDate: normalizeTextValue(normalizedPayload.requestDate),
    firstName: normalizeTextValue(normalizedPayload.firstName),
    lastName: normalizeTextValue(normalizedPayload.lastName),
    fatherName: normalizeTextValue(normalizedPayload.fatherName),
    companyName: normalizeTextValue(normalizedPayload.companyName),
    birthDayDate: normalizeTextValue(normalizedPayload.birthDayDate),
    idCardNO: normalizeTextValue(normalizedPayload.idCardNO),
    cardPlace: normalizeTextValue(normalizedPayload.cardPlace),
    nationalCode: normalizeTextValue(normalizedPayload.nationalCode),
    evidenceType: parseNumericValue(normalizedPayload.evidenceType, 0),
    evidenceTypeString: normalizeTextValue(
      normalizedPayload.evidenceTypeString,
    ),
    title: normalizeTextValue(normalizedPayload.title),
    count: normalizeTextValue(normalizedPayload.count),
    persianCount: normalizeTextValue(normalizedPayload.persianCount),
    fundTitle: normalizeTextValue(normalizedPayload.fundTitle),
    registerNO: normalizeNullableTextValue(normalizedPayload.registerNO),
    registerPlace: normalizeTextValue(normalizedPayload.registerPlace),
    registerDate: normalizeTextValue(normalizedPayload.registerDate),
  };
};

export const buildCertificateSentence = ({ certificate }) => {
  const holderTitle = certificate.companyName
    ? `شرکت ${certificate.companyName}`
    : `خانم/آقاى ${certificate.title || `${certificate.firstName} ${certificate.lastName}`.trim()}`;

  const countText = formatPersianNumber(certificate.count);

  return [
    holderTitle,
    `به شماره شناسنامه ${toPersianDigits(certificate.idCardNO)}`,
    `محل صدور ${certificate.cardPlace}`,
    `تاریخ تولد ${toPersianDigits(certificate.birthDayDate)}`,
    `نام پدر ${certificate.fatherName}`,
    `مالک ${countText} واحد سرمایه‌گذاری`,
    `از ${certificate.fundTitle} می‌باشد`,
  ]
    .filter(Boolean)
    .join(" ");
};

const wrapRtlParagraph = ({ text, font, fontSize, maxWidth }) => {
  const words = normalizeTextValue(text).split(/\s+/).filter(Boolean);
  const lines = [];
  let currentLine = "";

  for (const word of words) {
    const candidate = currentLine ? `${currentLine} ${word}` : word;
    const candidateWidth =
      (font.layout(candidate).positions || []).reduce(
        (sum, position) => sum + (position?.xAdvance || 0),
        0,
      ) *
      (fontSize / font.unitsPerEm);

    if (candidateWidth <= maxWidth || !currentLine) {
      currentLine = candidate;
      continue;
    }

    lines.push(currentLine);
    currentLine = word;
  }

  if (currentLine) {
    lines.push(currentLine);
  }

  return lines;
};

export const generateCertificatePdf = async ({
  certificate,
  certificateTrackId,
}) => {
  const assetPaths = resolveCertificateAssetPaths(certificate.fundRegisterNO);

  if (!assetPaths) {
    throw new Error(
      `قالب گواهی برای صندوق ${certificate.fundRegisterNO} تعریف نشده است.`,
    );
  }

  const { templatePath, signPath, sharedSignPath } = assetPaths;

  let templateBytes;
  let signBytes;
  let sharedSignBytes;
  let fontBytes;

  try {
    [templateBytes, signBytes, sharedSignBytes, fontBytes] = await Promise.all([
      fs.readFile(templatePath),
      fs.readFile(signPath),
      fs.readFile(sharedSignPath),
      fs.readFile(defaultFontPath),
    ]);
  } catch (error) {
    if (error?.code === "ENOENT") {
      throw new Error(
        `فایل‌های گواهی برای صندوق ${certificate.fundRegisterNO} در مسیر جدید یافت نشد.`,
      );
    }

    throw error;
  }

  const font = fontkit.create(fontBytes);

  const titleSize = 54;
  const metaSize = 38;
  const bodySize = 44;
  const disclaimerSize = 30;
  const lineHeight = 92;
  const paragraphRight = 3000;
  const paragraphWidth = 2400;
  const paragraphTop = 1280;
  const fundSignWidth = 620;
  const fundSignHeight = 300;
  const sharedSignWidth = 500;
  const sharedSignHeight = 300;
  const signGap = 80;
  const disclaimerLineHeight = 56;
  const disclaimerWidth = 2200;
  const disclaimerBottomInset = 350;
  const title = "گواهی مالکیت واحدهای سرمایه‌گذاری";
  const sentence = buildCertificateSentence({ certificate });

  const trackIdLine = `شماره رهگیری: ${toPersianDigits(certificateTrackId)}`;
  const certificateDate =
    formatCertificateDateFromCreatedAt(certificate.createdAt) ||
    certificate.workingDate;
  const dateLine = `تاریخ: ${toPersianDigits(certificateDate)}`;
  const bodyLines = wrapRtlParagraph({
    text: sentence,
    font,
    fontSize: bodySize,
    maxWidth: paragraphWidth,
  });
  const disclaimerLines = wrapRtlParagraph({
    text: CERTIFICATE_DISCLAIMER,
    font,
    fontSize: disclaimerSize,
    maxWidth: disclaimerWidth,
  });
  const backgroundBase64 = templateBytes.toString("base64");
  const signBase64 = signBytes.toString("base64");
  const sharedSignBase64 = sharedSignBytes.toString("base64");
  const signY = paragraphTop + bodyLines.length * lineHeight + 90;
  const totalSignWidth = sharedSignWidth + signGap + fundSignWidth;
  const signStartX = (pageWidth - totalSignWidth) / 2;
  const disclaimerStartY =
    pageHeight -
    disclaimerBottomInset -
    (Math.max(disclaimerLines.length, 1) - 1) * disclaimerLineHeight;
  const svg = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${pageWidth}" height="${pageHeight}" viewBox="0 0 ${pageWidth} ${pageHeight}">
  <image href="data:image/png;base64,${backgroundBase64}" x="0" y="0" width="${pageWidth}" height="${pageHeight}" />
  <text
    x="${pageWidth / 2}"
    y="900"
    text-anchor="middle"
    direction="rtl"
    unicode-bidi="plaintext"
    font-family="Vazirmatn UI FD NL"
    font-size="${titleSize}"
    fill="#1f1f1f"
  >${escapeXml(title)}</text>
  <text
    x="${pageWidth - 450}"
    y="1030"
    text-anchor="end"
    direction="rtl"
    unicode-bidi="plaintext"
    font-family="Vazirmatn UI FD NL"
    font-size="${metaSize}"
    fill="#3b3b3b"
  >${escapeXml(trackIdLine)}</text>
  <text
    x="450"
    y="1030"
    text-anchor="start"
    direction="rtl"
    unicode-bidi="plaintext"
    font-family="Vazirmatn UI FD NL"
    font-size="${metaSize}"
    fill="#3b3b3b"
  >${escapeXml(dateLine)}</text>
  ${bodyLines
    .map(
      (line, index) => `<text
    x="${paragraphRight}"
    y="${paragraphTop + index * lineHeight}"
    text-anchor="end"
    direction="rtl"
    unicode-bidi="plaintext"
    font-family="Vazirmatn UI FD NL"
    font-size="${bodySize}"
    fill="#181818"
  >${escapeXml(line)}</text>`,
    )
    .join("\n  ")}
  <image
    href="data:image/png;base64,${sharedSignBase64}"
    x="${signStartX}"
    y="${signY}"
    width="${sharedSignWidth}"
    height="${sharedSignHeight}"
  />
  <image
    href="data:image/png;base64,${signBase64}"
    x="${signStartX + sharedSignWidth + signGap}"
    y="${signY}"
    width="${fundSignWidth}"
    height="${fundSignHeight}"
  />
  ${disclaimerLines
    .map(
      (line, index) => `<text
    x="${pageWidth / 2}"
    y="${disclaimerStartY + index * disclaimerLineHeight}"
    text-anchor="middle"
    direction="rtl"
    unicode-bidi="plaintext"
    font-family="Vazirmatn UI FD NL"
    font-size="${disclaimerSize}"
    fill="#4a4a4a"
  >${escapeXml(line)}</text>`,
    )
    .join("\n  ")}
</svg>`;

  const resvg = new Resvg(svg, {
    fitTo: {
      mode: "width",
      value: pageWidth,
    },
    font: {
      fontFiles: [defaultFontPath],
      loadSystemFonts: false,
    },
  });

  const renderedPngBytes = resvg.render().asPng();
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage([pageWidth, pageHeight]);
  const renderedImage = await pdfDoc.embedPng(renderedPngBytes);
  page.drawImage(renderedImage, {
    x: 0,
    y: 0,
    width: pageWidth,
    height: pageHeight,
  });

  return {
    pdfBytes: await pdfDoc.save(),
    fileName: `فایل گواهی نامه ${certificateTrackId || certificate.serialNumber || certificate.nationalCode}.pdf`,
    renderedText: `${sentence} ${CERTIFICATE_DISCLAIMER}`.trim(),
  };
};
