import type { Invoice, InvoiceStatus } from "@/types";

export function formatInvoiceMoney(cents: number, currency = "EUR") {
  return new Intl.NumberFormat("de-DE", {
    style: "currency",
    currency,
  }).format(cents / 100);
}

export function formatInvoiceDate(value: string) {
  if (!value) return "—";
  return new Date(value).toLocaleDateString("de-DE", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
  });
}

export function sanitizeInvoiceFileSegment(value: string) {
  return value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9äöüß_-]+/gi, "-")
    .replace(/-+/g, "-")
    .replace(/^-|-$/g, "");
}

export function getInvoiceDocumentLabel(invoice: Pick<Invoice, "direction" | "document_type">) {
  if (invoice.document_type === "quote") return "Angebot";
  return invoice.direction === "incoming" ? "Eingangsrechnung" : "Rechnung";
}

export function resolveInvoiceStatus(
  invoice: Pick<Invoice, "document_type" | "due_date" | "status">,
): InvoiceStatus {
  if (invoice.status === "paid" || invoice.status === "cancelled") return invoice.status;
  if (invoice.status === "draft") return "draft";
  if (invoice.document_type === "quote") return invoice.status;

  const dueDate = new Date(`${invoice.due_date}T23:59:59`);
  if (!Number.isNaN(dueDate.getTime()) && dueDate.getTime() < Date.now()) {
    return "overdue";
  }

  return invoice.status;
}

function wrapText(doc: { splitTextToSize: (text: string, size: number) => string[] }, text: string, maxWidth: number) {
  return doc.splitTextToSize(text || "", maxWidth);
}

function hexToRgb(hex: string, fallback: [number, number, number] = [125, 211, 252]) {
  const normalized = hex.trim().replace("#", "");
  if (!/^[0-9a-f]{6}$/i.test(normalized)) return fallback;
  return [
    Number.parseInt(normalized.slice(0, 2), 16),
    Number.parseInt(normalized.slice(2, 4), 16),
    Number.parseInt(normalized.slice(4, 6), 16),
  ] as [number, number, number];
}

async function loadImageDataUrl(url: string) {
  const trimmed = url.trim();
  if (!trimmed) return null;
  if (trimmed.startsWith("data:image/")) return trimmed;

  try {
    const response = await fetch(trimmed);
    if (!response.ok) return null;
    const blob = await response.blob();
    const arrayBuffer = await blob.arrayBuffer();
    let base64 = "";

    if (typeof Buffer !== "undefined") {
      base64 = Buffer.from(arrayBuffer).toString("base64");
    } else {
      const bytes = new Uint8Array(arrayBuffer);
      let binary = "";
      bytes.forEach((byte) => {
        binary += String.fromCharCode(byte);
      });
      base64 = btoa(binary);
    }

    return `data:${blob.type || "image/png"};base64,${base64}`;
  } catch {
    return null;
  }
}

function renderInvoicePage(doc: unknown, invoice: Invoice, pageIndex: number, logoDataUrl?: string | null) {
  const pdf = doc as {
    addImage: (imageData: string, format: string, x: number, y: number, width: number, height: number) => unknown;
    addPage: () => void;
    internal: { pageSize: { getHeight: () => number; getWidth: () => number } };
    line: (x1: number, y1: number, x2: number, y2: number) => void;
    rect: (x: number, y: number, width: number, height: number, style?: string) => void;
    setDrawColor: (...args: Array<number | string>) => unknown;
    setFillColor: (...args: Array<number | string>) => unknown;
    setFont: (fontName: string, fontStyle?: string) => unknown;
    setFontSize: (size: number) => unknown;
    setTextColor: (...args: Array<number | string>) => unknown;
    splitTextToSize: (text: string, size: number) => string[];
    text: (text: string | string[], x: number, y: number, options?: Record<string, unknown>) => unknown;
  };

  if (pageIndex > 0) {
    pdf.addPage();
  }

  const pageWidth = pdf.internal.pageSize.getWidth();
  const pageHeight = pdf.internal.pageSize.getHeight();
  const margin = 16;
  const contentWidth = pageWidth - margin * 2;
  const status = resolveInvoiceStatus(invoice);
  const headlineColor =
    status === "paid" ? [52, 211, 153] : status === "overdue" ? [251, 113, 133] : hexToRgb(invoice.accent_color || "#7dd3fc");

  pdf.setFillColor(9, 8, 15);
  pdf.rect(0, 0, pageWidth, pageHeight, "F");

  pdf.setDrawColor(41, 37, 53);
  pdf.line(margin, 28, pageWidth - margin, 28);

  if (logoDataUrl) {
    const format = logoDataUrl.includes("image/png") ? "PNG" : logoDataUrl.includes("image/webp") ? "WEBP" : "JPEG";
    try {
      pdf.addImage(logoDataUrl, format, margin, 9, 16, 16);
    } catch {
      pdf.setFillColor(headlineColor[0], headlineColor[1], headlineColor[2]);
      pdf.rect(margin, 9, 16, 16, "F");
    }
  } else {
    pdf.setFillColor(headlineColor[0], headlineColor[1], headlineColor[2]);
    pdf.rect(margin, 9, 16, 16, "F");
  }

  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(17);
  pdf.setTextColor(255, 255, 255);
  pdf.text(invoice.company_name || "Mindavio", margin + 21, 16);

  const senderLine = [invoice.company_email, invoice.company_phone, invoice.company_website].filter(Boolean).join(" · ");
  if (senderLine) {
    pdf.setFont("helvetica", "normal");
    pdf.setFontSize(8);
    pdf.setTextColor(160, 159, 179);
    pdf.text(senderLine, margin + 21, 22);
  }

  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(10);
  pdf.setTextColor(160, 159, 179);
  pdf.text(getInvoiceDocumentLabel(invoice), pageWidth - margin, 18, {
    align: "right",
  });

  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(28);
  pdf.setTextColor(headlineColor[0], headlineColor[1], headlineColor[2]);
  pdf.text(invoice.invoice_number, margin, 44);

  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(11);
  pdf.setTextColor(220, 220, 230);
  const title = invoice.title?.trim()
    ? invoice.title.trim()
    : getInvoiceDocumentLabel(invoice);
  pdf.text(title, margin, 52);

  pdf.setTextColor(160, 159, 179);
  pdf.text(`Status: ${status}`, pageWidth - margin, 40, { align: "right" });
  pdf.text(`Ausgestellt: ${formatInvoiceDate(invoice.issue_date)}`, pageWidth - margin, 47, { align: "right" });
  pdf.text(`${invoice.document_type === "quote" ? "Gültig bis" : "Fällig"}: ${formatInvoiceDate(invoice.due_date)}`, pageWidth - margin, 54, { align: "right" });

  pdf.setDrawColor(41, 37, 53);
  pdf.setFillColor(17, 16, 26);
  pdf.rect(margin, 64, contentWidth, 34, "FD");

  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(10);
  pdf.setTextColor(145, 145, 170);
  pdf.text(invoice.document_type === "quote" ? "Angebotsempfänger" : invoice.direction === "incoming" ? "Rechnungssteller" : "Rechnungsempfänger", margin + 4, 72);

  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(13);
  pdf.setTextColor(255, 255, 255);
  pdf.text(invoice.counterparty_name || "—", margin + 4, 81);

  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(10);
  pdf.setTextColor(190, 190, 205);
  const addressLines = [
    invoice.counterparty_email?.trim() || "",
    ...(invoice.counterparty_address?.trim() ? invoice.counterparty_address.trim().split("\n") : []),
  ].filter(Boolean);
  pdf.text(addressLines.length > 0 ? addressLines : ["—"], margin + 4, 88);

  const companyLines = [
    ...(invoice.company_address?.trim() ? invoice.company_address.trim().split("\n") : []),
    invoice.company_vat_id?.trim() || "",
  ].filter(Boolean);
  if (companyLines.length > 0) {
    pdf.setFont("helvetica", "normal");
    pdf.setFontSize(8);
    pdf.setTextColor(125, 125, 145);
    pdf.text(wrapText(pdf, companyLines.join(" · "), contentWidth), margin, 104);
  }

  let y = 110;
  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(9);
  pdf.setTextColor(145, 145, 170);
  pdf.text("Beschreibung", margin, y);
  pdf.text("Menge", pageWidth - 78, y, { align: "right" });
  pdf.text("Preis", pageWidth - 48, y, { align: "right" });
  pdf.text("Gesamt", pageWidth - margin, y, { align: "right" });

  y += 4;
  pdf.setDrawColor(41, 37, 53);
  pdf.line(margin, y, pageWidth - margin, y);
  y += 8;

  const items = invoice.items || [];
  items.forEach((item) => {
    const descriptionLines = wrapText(pdf, item.description || "—", contentWidth - 70);
    const rowHeight = Math.max(7, descriptionLines.length * 5);

    pdf.setFont("helvetica", "normal");
    pdf.setFontSize(10);
    pdf.setTextColor(240, 240, 248);
    pdf.text(descriptionLines, margin, y);
    pdf.text(String(item.quantity), pageWidth - 78, y, { align: "right" });
    pdf.text(formatInvoiceMoney(item.unit_price_cents, invoice.currency), pageWidth - 48, y, {
      align: "right",
    });
    pdf.text(formatInvoiceMoney(Math.round(item.quantity * item.unit_price_cents), invoice.currency), pageWidth - margin, y, {
      align: "right",
    });

    y += rowHeight;
    pdf.setDrawColor(28, 26, 38);
    pdf.line(margin, y, pageWidth - margin, y);
    y += 6;
  });

  const totalsBoxTop = Math.max(y + 4, 180);
  pdf.setDrawColor(41, 37, 53);
  pdf.setFillColor(17, 16, 26);
  pdf.rect(pageWidth - 82, totalsBoxTop, 66, 34, "FD");

  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(10);
  pdf.setTextColor(170, 170, 185);
  pdf.text("Netto", pageWidth - 78, totalsBoxTop + 8);
  pdf.text(formatInvoiceMoney(invoice.subtotal_cents, invoice.currency), pageWidth - 20, totalsBoxTop + 8, {
    align: "right",
  });
  pdf.text(invoice.tax_enabled === false ? "MwSt. nicht berechnet" : `MwSt. ${invoice.tax_rate}%`, pageWidth - 78, totalsBoxTop + 16);
  pdf.text(formatInvoiceMoney(invoice.tax_cents, invoice.currency), pageWidth - 20, totalsBoxTop + 16, {
    align: "right",
  });

  pdf.setDrawColor(41, 37, 53);
  pdf.line(pageWidth - 78, totalsBoxTop + 21, pageWidth - 20, totalsBoxTop + 21);

  pdf.setFont("helvetica", "bold");
  pdf.setFontSize(12);
  pdf.setTextColor(headlineColor[0], headlineColor[1], headlineColor[2]);
  pdf.text("Gesamt", pageWidth - 78, totalsBoxTop + 30);
  pdf.text(formatInvoiceMoney(invoice.total_cents, invoice.currency), pageWidth - 20, totalsBoxTop + 30, {
    align: "right",
  });

  if (invoice.notes?.trim()) {
    const notesTop = Math.min(totalsBoxTop + 48, pageHeight - 42);
    pdf.setFont("helvetica", "bold");
    pdf.setFontSize(10);
    pdf.setTextColor(145, 145, 170);
    pdf.text("Anmerkungen", margin, notesTop);
    pdf.setFont("helvetica", "normal");
    pdf.setFontSize(10);
    pdf.setTextColor(190, 190, 205);
    pdf.text(wrapText(pdf, invoice.notes, contentWidth), margin, notesTop + 8);
  }

  pdf.setDrawColor(41, 37, 53);
  pdf.line(margin, pageHeight - 20, pageWidth - margin, pageHeight - 20);
  pdf.setFont("helvetica", "normal");
  pdf.setFontSize(9);
  pdf.setTextColor(125, 125, 145);
  pdf.text(invoice.company_website || "mindavio.app", margin, pageHeight - 12);
  pdf.text(`Generiert am ${new Date().toLocaleDateString("de-DE")}`, pageWidth - margin, pageHeight - 12, {
    align: "right",
  });
}

export async function buildInvoicePdfBlob(invoice: Invoice) {
  const { default: jsPDF } = await import("jspdf");
  const doc = new jsPDF({
    format: "a4",
    unit: "mm",
  });

  const logoDataUrl = await loadImageDataUrl(invoice.logo_url || "");
  renderInvoicePage(doc, invoice, 0, logoDataUrl);
  return doc.output("blob");
}

export async function buildInvoicePackPdfBlob(invoices: Invoice[]) {
  const { default: jsPDF } = await import("jspdf");
  const doc = new jsPDF({
    format: "a4",
    unit: "mm",
  });

  for (let index = 0; index < invoices.length; index += 1) {
    const invoice = invoices[index];
    const logoDataUrl = await loadImageDataUrl(invoice.logo_url || "");
    renderInvoicePage(doc, invoice, index, logoDataUrl);
  }

  return doc.output("blob");
}
