(() => {
  const PAGE_WIDTH = 595.28;
  const PAGE_HEIGHT = 841.89;
  const CANVAS_SCALE = 2;
  const MARGIN = 44;

  function wrapText(context, value, maxWidth) {
    const text = String(value ?? "").replace(/\s+/g, " ").trim() || "-";
    const words = text.split(" ");
    const lines = [];
    let currentLine = "";

    const pushLongWord = (word) => {
      let remaining = word;
      while (remaining && context.measureText(remaining).width > maxWidth) {
        let fitted = "";
        for (const character of remaining) {
          if (fitted && context.measureText(fitted + character).width > maxWidth) break;
          fitted += character;
        }
        if (!fitted) fitted = remaining[0];
        lines.push(fitted);
        remaining = remaining.slice(fitted.length);
      }
      return remaining;
    };

    words.forEach((word) => {
      const candidate = currentLine ? `${currentLine} ${word}` : word;
      if (context.measureText(candidate).width <= maxWidth) {
        currentLine = candidate;
        return;
      }
      if (currentLine) lines.push(currentLine);
      currentLine = pushLongWord(word);
    });

    if (currentLine) lines.push(currentLine);
    return lines.length > 0 ? lines : ["-"];
  }

  function limitLines(context, lines, maxLines, maxWidth) {
    if (lines.length <= maxLines) return lines;
    const limited = lines.slice(0, maxLines);
    let finalLine = limited[maxLines - 1];
    while (finalLine && context.measureText(`${finalLine}...`).width > maxWidth) {
      finalLine = finalLine.slice(0, -1).trimEnd();
    }
    limited[maxLines - 1] = `${finalLine}...`;
    return limited;
  }

  function getTypography(columnCount) {
    if (columnCount <= 3) return { bodySize: 21, headerSize: 18, lineHeight: 27, maxLines: 6 };
    if (columnCount <= 5) return { bodySize: 18, headerSize: 16, lineHeight: 23, maxLines: 5 };
    return { bodySize: 15, headerSize: 13, lineHeight: 20, maxLines: 4 };
  }

  function createCanvasPage(title, meta, columns, columnWidths, typography, includeTableHeader = true) {
    const canvas = document.createElement("canvas");
    canvas.width = Math.round(PAGE_WIDTH * CANVAS_SCALE);
    canvas.height = Math.round(PAGE_HEIGHT * CANVAS_SCALE);
    const context = canvas.getContext("2d");
    if (!context) throw new Error("Przeglądarka nie może przygotować stron PDF.");

    const contentWidth = canvas.width - (MARGIN * 2);
    context.fillStyle = "#ffffff";
    context.fillRect(0, 0, canvas.width, canvas.height);
    context.textBaseline = "alphabetic";

    let titleSize = 28;
    context.font = `800 ${titleSize}px Arial, sans-serif`;
    while (titleSize > 19 && context.measureText(title).width > contentWidth) {
      titleSize -= 1;
      context.font = `800 ${titleSize}px Arial, sans-serif`;
    }
    context.fillStyle = "#17352b";
    context.fillText(title, MARGIN, 58, contentWidth);

    context.font = "400 14px Arial, sans-serif";
    context.fillStyle = "#586660";
    context.fillText(meta, MARGIN, 84, contentWidth);
    context.fillStyle = "#00a76f";
    context.fillRect(MARGIN, 101, contentWidth, 4);

    let y = 122;
    if (includeTableHeader) {
      context.font = `800 ${typography.headerSize}px Arial, sans-serif`;
      const headerLines = columns.map((column, index) => (
        limitLines(
          context,
          wrapText(context, column.label.toLocaleUpperCase("pl"), columnWidths[index] - 14),
          3,
          columnWidths[index] - 14,
        )
      ));
      const headerHeight = Math.max(
        38,
        Math.max(...headerLines.map((lines) => lines.length)) * (typography.headerSize + 4) + 16,
      );

      let x = MARGIN;
      headerLines.forEach((lines, index) => {
        const width = columnWidths[index];
        context.fillStyle = "#e9f7f1";
        context.fillRect(x, y, width, headerHeight);
        context.strokeStyle = "#a9cfc0";
        context.lineWidth = 1;
        context.strokeRect(x + 0.5, y + 0.5, width, headerHeight);
        context.fillStyle = "#17352b";
        context.font = `800 ${typography.headerSize}px Arial, sans-serif`;
        lines.forEach((line, lineIndex) => {
          context.fillText(
            line,
            x + 7,
            y + 10 + typography.headerSize + (lineIndex * (typography.headerSize + 4)),
            width - 14,
          );
        });
        x += width;
      });
      y += headerHeight;
    }

    return {
      canvas,
      context,
      contentWidth,
      contentBottom: canvas.height - 66,
      y,
    };
  }

  function prepareRow(context, values, columnWidths, typography) {
    context.font = `600 ${typography.bodySize}px Arial, sans-serif`;
    const cells = values.map((value, index) => (
      limitLines(
        context,
        wrapText(context, value, columnWidths[index] - 14),
        typography.maxLines,
        columnWidths[index] - 14,
      )
    ));
    const rowHeight = Math.max(
      36,
      Math.max(...cells.map((lines) => lines.length)) * typography.lineHeight + 14,
    );
    return { cells, rowHeight };
  }

  function drawRow(pageState, rowLayout, columnWidths, typography, rowIndex) {
    const { context, y } = pageState;
    let x = MARGIN;
    rowLayout.cells.forEach((lines, index) => {
      const width = columnWidths[index];
      context.fillStyle = rowIndex % 2 === 0 ? "#ffffff" : "#f8fbfa";
      context.fillRect(x, y, width, rowLayout.rowHeight);
      context.strokeStyle = "#cfd5d2";
      context.lineWidth = 1;
      context.strokeRect(x + 0.5, y + 0.5, width, rowLayout.rowHeight);
      context.fillStyle = "#1f2925";
      context.font = `600 ${typography.bodySize}px Arial, sans-serif`;
      lines.forEach((line, lineIndex) => {
        context.fillText(
          line,
          x + 7,
          y + 8 + typography.bodySize + (lineIndex * typography.lineHeight),
          width - 14,
        );
      });
      x += width;
    });
    pageState.y += rowLayout.rowHeight;
  }

  function drawPremiumSummary(pageState, totalPremium) {
    const { context, contentWidth } = pageState;
    const summaryWidth = Math.min(430, contentWidth);
    const summaryHeight = 54;
    const x = MARGIN + contentWidth - summaryWidth;
    const y = pageState.y + 16;
    context.fillStyle = "#f0faf6";
    context.fillRect(x, y, summaryWidth, summaryHeight);
    context.strokeStyle = "#a9cfc0";
    context.lineWidth = 1;
    context.strokeRect(x + 0.5, y + 0.5, summaryWidth, summaryHeight);
    context.fillStyle = "#00a76f";
    context.fillRect(x, y, 7, summaryHeight);
    context.fillStyle = "#17352b";
    context.font = "700 16px Arial, sans-serif";
    context.fillText("Łączna suma składek", x + 22, y + 33);
    context.font = "800 20px Arial, sans-serif";
    context.textAlign = "right";
    context.fillText(totalPremium, x + summaryWidth - 18, y + 34);
    context.textAlign = "left";
    pageState.y = y + summaryHeight;
  }

  function finishPages(pageStates) {
    pageStates.forEach((pageState, index) => {
      const { context, canvas, contentWidth } = pageState;
      context.strokeStyle = "#d8dfdc";
      context.lineWidth = 1;
      context.beginPath();
      context.moveTo(MARGIN, canvas.height - 45);
      context.lineTo(MARGIN + contentWidth, canvas.height - 45);
      context.stroke();
      context.fillStyle = "#6b7772";
      context.font = "400 13px Arial, sans-serif";
      context.textAlign = "right";
      context.fillText(`Strona ${index + 1} z ${pageStates.length}`, MARGIN + contentWidth, canvas.height - 23);
      context.textAlign = "left";
    });
  }

  async function create(options) {
    if (!globalThis.PDFLib?.PDFDocument) {
      throw new Error("Brakuje lokalnego modułu do tworzenia PDF.");
    }
    const columns = Array.isArray(options.columns) ? options.columns : [];
    const rows = Array.isArray(options.rows) ? options.rows : [];
    if (columns.length === 0 || rows.length === 0) {
      throw new Error("Brak danych do zapisania w PDF.");
    }

    const canvasWidth = Math.round(PAGE_WIDTH * CANVAS_SCALE);
    const availableWidth = canvasWidth - (MARGIN * 2);
    const totalWeight = columns.reduce((sum, column) => sum + Number(column.weight || 1), 0);
    const columnWidths = columns.map((column) => (Number(column.weight || 1) / totalWeight) * availableWidth);
    const typography = getTypography(columns.length);
    const pageStates = [];
    let pageState = createCanvasPage(options.title, options.meta, columns, columnWidths, typography);
    pageStates.push(pageState);

    rows.forEach((values, rowIndex) => {
      let rowLayout = prepareRow(pageState.context, values, columnWidths, typography);
      if (pageState.y + rowLayout.rowHeight > pageState.contentBottom) {
        pageState = createCanvasPage(options.title, options.meta, columns, columnWidths, typography);
        pageStates.push(pageState);
        rowLayout = prepareRow(pageState.context, values, columnWidths, typography);
      }
      drawRow(pageState, rowLayout, columnWidths, typography, rowIndex);
    });

    if (options.totalPremium) {
      if (pageState.y + 86 > pageState.contentBottom) {
        pageState = createCanvasPage(options.title, options.meta, columns, columnWidths, typography, false);
        pageStates.push(pageState);
      }
      drawPremiumSummary(pageState, options.totalPremium);
    }

    finishPages(pageStates);
    const pdfDocument = await globalThis.PDFLib.PDFDocument.create();
    pdfDocument.setTitle(options.fileTitle || options.title);
    pdfDocument.setSubject("Sekagent - ewidencja polis");
    if (options.generatedAt instanceof Date) {
      pdfDocument.setCreationDate(options.generatedAt);
      pdfDocument.setModificationDate(options.generatedAt);
    }

    for (const state of pageStates) {
      const image = await pdfDocument.embedPng(state.canvas.toDataURL("image/png"));
      const page = pdfDocument.addPage([PAGE_WIDTH, PAGE_HEIGHT]);
      page.drawImage(image, { x: 0, y: 0, width: PAGE_WIDTH, height: PAGE_HEIGHT });
    }

    return pdfDocument.save({ useObjectStreams: false, updateFieldAppearances: false });
  }

  globalThis.PoliciesPdfRenderer = Object.freeze({ create });
})();
