import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import x3LogoUrl from "@/assets/x3-logo.png";

import type { ProposalDetail, ProposalSummary } from "@/lib/mock/types";
import { formatBRL, formatDate } from "@/lib/format";
import { getEffectiveStatus } from "@/lib/proposal-status";

// X3 palette (RGB)
const COLOR_BRAND: [number, number, number] = [25, 122, 250]; // #197AFA
const COLOR_CYAN: [number, number, number] = [4, 148, 252]; // #0494FC
const COLOR_MINT: [number, number, number] = [38, 226, 200]; // #26E2C8
const COLOR_INK: [number, number, number] = [17, 24, 39];
const COLOR_MUTED: [number, number, number] = [107, 114, 128];
const COLOR_BORDER: [number, number, number] = [229, 231, 235];
const COLOR_SOFT_BG: [number, number, number] = [246, 249, 255];

const paymentLabel: Record<ProposalDetail["paymentMethod"], string> = {
  pix: "Pix",
  boleto: "Boleto bancário",
  cartao: "Cartão de crédito",
};

const statusLabel: Record<ProposalSummary["status"], string> = {
  rascunho: "Rascunho",
  enviada: "Enviada",
  aprovada: "Aprovada",
  expirada: "Vencida",
};

const MARGIN_X = 40;
const PAGE_TOP = 95; // below header
const FOOTER_H = 50;

export async function generateProposalPdf(
  summary: ProposalSummary,
  detail: ProposalDetail,
) {
  const logo = await loadLogo();
  const { doc, filename } = buildProposalPdf(summary, detail, logo);
  doc.save(filename);
}

export function buildProposalPdf(
  summary: ProposalSummary,
  detail: ProposalDetail,
  logo?: { dataUrl: string; width: number; height: number },
) {
  const doc = new jsPDF({ unit: "pt", format: "a4" });
  const pageWidth = doc.internal.pageSize.getWidth();
  const pageHeight = doc.internal.pageSize.getHeight();
  const contentWidth = pageWidth - MARGIN_X * 2;
  const bottomLimit = pageHeight - FOOTER_H - 10;

  // ---------- Totals ----------
  const oneTimeTotal = detail.items
    .filter((i) => !i.recurring)
    .reduce((s, i) => s + i.unitPrice * i.quantity, 0);
  const recurringTotal = detail.items
    .filter((i) => i.recurring === "mensal")
    .reduce((s, i) => s + i.unitPrice * i.quantity, 0);
  const installmentValue =
    detail.installments > 0 ? oneTimeTotal / detail.installments : oneTimeTotal;

  let cursorY = PAGE_TOP;

  // ---------- Helpers ----------
  const ensureSpace = (needed: number) => {
    if (cursorY + needed > bottomLimit) {
      doc.addPage();
      drawHeader();
      cursorY = PAGE_TOP;
    }
  };

  const drawSectionTitle = (title: string, opts?: { topGap?: number }) => {
    const topGap = opts?.topGap ?? 14;
    ensureSpace(34 + topGap);
    cursorY += topGap;
    doc.setFont("helvetica", "bold");
    doc.setFontSize(11);
    doc.setTextColor(...COLOR_BRAND);
    doc.text(title.toUpperCase(), MARGIN_X, cursorY);
    doc.setDrawColor(...COLOR_BORDER);
    doc.setLineWidth(0.6);
    doc.line(MARGIN_X, cursorY + 5, pageWidth - MARGIN_X, cursorY + 5);
    cursorY += 18;
    doc.setTextColor(...COLOR_INK);
    doc.setFont("helvetica", "normal");
  };

  const drawParagraph = (text: string, opts?: { size?: number; color?: [number, number, number] }) => {
    const size = opts?.size ?? 10;
    const color = opts?.color ?? COLOR_INK;
    doc.setFont("helvetica", "normal");
    doc.setFontSize(size);
    doc.setTextColor(...color);
    const lines = doc.splitTextToSize(text, contentWidth) as string[];
    const lineHeight = size * 1.4;
    for (const line of lines) {
      ensureSpace(lineHeight);
      doc.text(line, MARGIN_X, cursorY);
      cursorY += lineHeight;
    }
  };

  const drawKeyValueGrid = (
    rows: { label: string; value: string }[],
    cols = 2,
  ) => {
    const rowH = 38;
    const colW = contentWidth / cols;
    for (let i = 0; i < rows.length; i += cols) {
      ensureSpace(rowH + 6);
      for (let c = 0; c < cols; c++) {
        const item = rows[i + c];
        if (!item) continue;
        const x = MARGIN_X + c * colW;
        // card
        doc.setFillColor(...COLOR_SOFT_BG);
        doc.setDrawColor(...COLOR_BORDER);
        doc.roundedRect(x, cursorY, colW - 8, rowH - 4, 4, 4, "FD");
        doc.setFont("helvetica", "normal");
        doc.setFontSize(7.5);
        doc.setTextColor(...COLOR_MUTED);
        doc.text(item.label.toUpperCase(), x + 10, cursorY + 13);
        doc.setFont("helvetica", "bold");
        doc.setFontSize(10);
        doc.setTextColor(...COLOR_INK);
        const valueLines = doc.splitTextToSize(item.value, colW - 24) as string[];
        doc.text(valueLines[0] ?? "", x + 10, cursorY + 27);
      }
      cursorY += rowH;
    }
  };

  // ---------- HEADER (drawn per page via afterPageContent hook) ----------
  const drawHeader = () => {
    // top brand band
    doc.setFillColor(...COLOR_BRAND);
    doc.rect(0, 0, pageWidth, 6, "F");

    // colored logo
    if (logo) {
      const targetH = 26;
      const targetW = (logo.width / logo.height) * targetH;
      doc.addImage(
        logo.dataUrl,
        "PNG",
        MARGIN_X,
        20,
        targetW,
        targetH,
        undefined,
        "FAST",
      );
    } else {
      doc.setFont("helvetica", "bold");
      doc.setFontSize(18);
      doc.setTextColor(...COLOR_INK);
      doc.text("X3", MARGIN_X, 38);
      doc.setTextColor(...COLOR_BRAND);
      doc.text("Sites", MARGIN_X + 24, 38);
    }

    doc.setFont("helvetica", "normal");
    doc.setFontSize(8);
    doc.setTextColor(...COLOR_MUTED);
    doc.text("Proposta comercial", MARGIN_X, 58);

    // right block
    const rightX = pageWidth - MARGIN_X;
    doc.setFont("helvetica", "bold");
    doc.setFontSize(10);
    doc.setTextColor(...COLOR_INK);
    doc.text(`Proposta #${summary.number}`, rightX, 32, { align: "right" });
    doc.setFont("helvetica", "normal");
    doc.setFontSize(8);
    doc.setTextColor(...COLOR_MUTED);
    doc.text(`Emitida em ${formatDate(todayISO())}`, rightX, 46, {
      align: "right",
    });
    doc.text(`Válida até ${formatDate(summary.validUntil)}`, rightX, 58, {
      align: "right",
    });

    // separator
    doc.setDrawColor(...COLOR_BORDER);
    doc.setLineWidth(0.5);
    doc.line(MARGIN_X, 70, pageWidth - MARGIN_X, 70);
  };

  const drawFooter = (pageNumber: number, totalPages: number) => {
    const y = pageHeight - FOOTER_H + 10;
    doc.setDrawColor(...COLOR_BORDER);
    doc.setLineWidth(0.5);
    doc.line(MARGIN_X, y, pageWidth - MARGIN_X, y);

    doc.setFont("helvetica", "bold");
    doc.setFontSize(8);
    doc.setTextColor(...COLOR_INK);
    doc.text("X3 Consultoria e Desenvolvimento LTDA", MARGIN_X, y + 14);

    doc.setFont("helvetica", "normal");
    doc.setTextColor(...COLOR_MUTED);
    doc.text("CNPJ: 45.389.556/0001-50  ·  www.x3sites.com.br", MARGIN_X, y + 26);

    doc.text(
      `Página ${pageNumber} de ${totalPages}`,
      pageWidth - MARGIN_X,
      y + 26,
      { align: "right" },
    );
  };

  // ---------- BODY ----------

  drawHeader();

  // Title block
  doc.setFont("helvetica", "bold");
  doc.setFontSize(20);
  doc.setTextColor(...COLOR_INK);
  const titleLines = doc.splitTextToSize(summary.title, contentWidth) as string[];
  for (const ln of titleLines) {
    ensureSpace(24);
    doc.text(ln, MARGIN_X, cursorY);
    cursorY += 24;
  }
  cursorY += 4;

  // Client + status row
  doc.setFont("helvetica", "normal");
  doc.setFontSize(10);
  doc.setTextColor(...COLOR_MUTED);
  doc.text(`Preparada para`, MARGIN_X, cursorY);
  doc.setFont("helvetica", "bold");
  doc.setTextColor(...COLOR_INK);
  doc.text(summary.clientName, MARGIN_X + 78, cursorY);

  // Status pill (right)
  const status = statusLabel[getEffectiveStatus(summary)];
  doc.setFontSize(8);
  const pillW = doc.getTextWidth(status) + 16;
  const pillX = pageWidth - MARGIN_X - pillW;
  doc.setFillColor(...COLOR_SOFT_BG);
  doc.setDrawColor(...COLOR_BRAND);
  doc.roundedRect(pillX, cursorY - 10, pillW, 16, 8, 8, "FD");
  doc.setTextColor(...COLOR_BRAND);
  doc.setFont("helvetica", "bold");
  doc.text(status, pillX + 8, cursorY + 1);
  cursorY += 24;

  // Resumo cards
  drawKeyValueGrid(
    [
      { label: "Cliente", value: summary.clientName },
      { label: "Responsável", value: detail.contactName || "—" },
      { label: "E-mail", value: detail.email || "—" },
      { label: "WhatsApp", value: detail.whatsapp || "—" },
    ],
    2,
  );
  cursorY += 6;

  // ---------- Escopo ----------
  drawSectionTitle("Escopo do projeto");
  drawParagraph(detail.scope || "—", { color: COLOR_INK });
  cursorY += 6;

  // ---------- Serviços (table) ----------
  drawSectionTitle("Serviços incluídos");

  autoTable(doc, {
    startY: cursorY,
    margin: { left: MARGIN_X, right: MARGIN_X, bottom: FOOTER_H + 10 },
    head: [["Serviço", "Qtd.", "Valor unit.", "Subtotal"]],
    body: detail.items.map((it) => [
      `${it.name}${it.recurring ? `  (${it.recurring})` : ""}`,
      String(it.quantity),
      formatBRL(it.unitPrice),
      `${formatBRL(it.unitPrice * it.quantity)}${it.recurring === "mensal" ? "/mês" : ""}`,
    ]),
    styles: {
      font: "helvetica",
      fontSize: 9.5,
      cellPadding: 8,
      textColor: COLOR_INK,
      lineColor: COLOR_BORDER,
      lineWidth: 0.4,
    },
    headStyles: {
      fillColor: COLOR_BRAND,
      textColor: [255, 255, 255],
      fontStyle: "bold",
      fontSize: 9,
    },
    alternateRowStyles: { fillColor: COLOR_SOFT_BG },
    columnStyles: {
      0: { cellWidth: "auto" },
      1: { halign: "right", cellWidth: 50 },
      2: { halign: "right", cellWidth: 90 },
      3: { halign: "right", cellWidth: 110, fontStyle: "bold" },
    },
    didDrawPage: () => {
      // header is redrawn on new pages by re-running our header drawer
      drawHeader();
    },
  });
  // jspdf-autotable attaches lastAutoTable
  // @ts-expect-error: lastAutoTable typing
  cursorY = (doc.lastAutoTable?.finalY ?? cursorY) + 14;

  // ---------- Entregas ----------
  drawSectionTitle("Entregas");
  const deliverables = detail.deliverables.filter((d) => d.trim() !== "");
  doc.setFont("helvetica", "normal");
  doc.setFontSize(10);
  doc.setTextColor(...COLOR_INK);
  for (const d of deliverables) {
    const lines = doc.splitTextToSize(d, contentWidth - 18) as string[];
    const blockH = lines.length * 13 + 4;
    ensureSpace(blockH);
    doc.setFillColor(...COLOR_MINT);
    doc.circle(MARGIN_X + 4, cursorY - 3, 2.4, "F");
    for (let i = 0; i < lines.length; i++) {
      doc.text(lines[i], MARGIN_X + 14, cursorY + i * 13);
    }
    cursorY += blockH;
  }
  cursorY += 4;

  // ---------- Investimento ----------
  drawSectionTitle("Investimento");

  const methods =
    detail.paymentMethods && detail.paymentMethods.length > 0
      ? detail.paymentMethods
      : [detail.paymentMethod];
  const paymentLines = methods
    .map((m) =>
      m === "cartao"
        ? `Cartão de crédito${detail.installments > 1 ? ` em até ${detail.installments}x de ${formatBRL(installmentValue)}` : ""}`
        : paymentLabel[m],
    )
    .join(" | ");

  const investRows: { label: string; value: string }[] = [
    { label: "Valor total único", value: formatBRL(oneTimeTotal) },
    { label: "Formas de pagamento", value: paymentLines },
    {
      label: "Parcelamento",
      value: methods.includes("cartao") && detail.installments > 1
        ? `${detail.installments}x de ${formatBRL(installmentValue)} no cartão`
        : "À vista",
    },
    {
      label: "Recorrência mensal",
      value: recurringTotal > 0 ? `${formatBRL(recurringTotal)}/mês` : "—",
    },
  ];
  drawKeyValueGrid(investRows, 2);
  cursorY += 6;

  // ---------- Prazo ----------
  drawSectionTitle("Prazo e validade");
  drawKeyValueGrid(
    [
      { label: "Prazo de entrega", value: `${detail.deadlineDays} dias úteis` },
      { label: "Validade da proposta", value: formatDate(summary.validUntil) },
    ],
    2,
  );
  cursorY += 6;

  // ---------- Observações ----------
  if (detail.observations && detail.observations.trim() !== "") {
    drawSectionTitle("Observações");
    drawParagraph(detail.observations, { color: COLOR_INK });
  }

  // ---------- Footer on all pages ----------
  const totalPages = doc.getNumberOfPages();
  for (let i = 1; i <= totalPages; i++) {
    doc.setPage(i);
    drawFooter(i, totalPages);
  }

  // ---------- Return doc + filename ----------
  const filename = `proposta-${summary.number}-${slug(summary.clientName)}.pdf`;
  return { doc, filename };
}

function todayISO(): string {
  const d = new Date();
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}

function slug(s: string): string {
  return s
    .toLowerCase()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/(^-|-$)/g, "")
    .slice(0, 40);
}

let cachedLogo: { dataUrl: string; width: number; height: number } | null = null;

async function loadLogo() {
  if (cachedLogo) return cachedLogo;
  if (typeof window === "undefined") return undefined;
  try {
    const res = await fetch(x3LogoUrl);
    const blob = await res.blob();
    const dataUrl = await new Promise<string>((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = reject;
      reader.readAsDataURL(blob);
    });
    const { width, height } = await new Promise<{ width: number; height: number }>(
      (resolve, reject) => {
        const img = new Image();
        img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
        img.onerror = reject;
        img.src = dataUrl;
      },
    );
    cachedLogo = { dataUrl, width, height };
    return cachedLogo;
  } catch {
    return undefined;
  }
}
