/* ============================================================
   File Preview Renderers — ported from POC Preview file
   Real engines: PDF.js, SheetJS, PapaParse, canvas-datagrid,
                 docx-preview → html2canvas → jsPDF → PDF.js
   Exposes window.FilePreview = { render, methodOf, ... }
   ============================================================ */

/* Configure PDF.js worker once */
if (typeof pdfjsLib !== "undefined") {
  pdfjsLib.GlobalWorkerOptions.workerSrc =
    "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js";
}

const PREVIEW_SUPPORTED = [".xlsx",".xlsm",".xlsb",".csv",".tsv",".txt",".docx",".pdf",".png",".jpeg",".jpg"];
const DOWNLOAD_ONLY = [".doc",".xls"];

/* Method label per file type */
const methodOf = (ext) => {
  ext = ext.toLowerCase();
  if ([".xlsx",".xlsm",".xlsb"].includes(ext)) return "SheetJS + Canvas DataGrid";
  if ([".csv",".tsv"].includes(ext))           return "PapaParse + Canvas DataGrid";
  if (ext === ".pdf")                          return "PDF.js";
  if (ext === ".docx")                         return "docx-preview → html2canvas → jsPDF → PDF.js";
  if ([".png",".jpeg",".jpg"].includes(ext))   return "Native browser";
  if (ext === ".txt")                          return "Plain text";
  if (DOWNLOAD_ONLY.includes(ext))             return "Download only";
  return "Unsupported";
};

/* Fetch a URL → ArrayBuffer */
async function fetchAB(url) {
  const resp = await fetch(url);
  if (!resp.ok) throw new Error(`Fetch failed: ${resp.status} ${resp.statusText}`);
  return await resp.arrayBuffer();
}

/* Resolve source → { arrayBuffer, url, blob } */
async function resolveSource(source) {
  if (!source) throw new Error("No source");
  if (source instanceof File || source instanceof Blob) {
    return { arrayBuffer: await source.arrayBuffer(), url: URL.createObjectURL(source), blob: source };
  }
  if (typeof source === "string") {
    const ab = await fetchAB(source);
    return { arrayBuffer: ab, url: source, blob: null };
  }
  throw new Error("Unsupported source type");
}

/* ─── Image ─── */
async function renderImage(source, target) {
  const { url } = await resolveSource(source);
  target.innerHTML = `<img src="${url}" style="max-width:100%;height:auto;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.08)"/>`;
}

/* ─── PDF ─── */
async function renderPDF(source, target, opts = {}) {
  const { arrayBuffer } = await resolveSource(source);
  const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
  const totalPages = pdf.numPages;
  const maxPages = Math.min(totalPages, opts.maxPages || 20);

  target.innerHTML = `<div class="pdf-pages"></div>${
    totalPages > maxPages ? `<div class="fp-warning">Showing first ${maxPages} of ${totalPages} pages</div>` : ""
  }`;
  const pagesDiv = target.querySelector(".pdf-pages");

  for (let i = 1; i <= maxPages; i++) {
    const page = await pdf.getPage(i);
    const viewport = page.getViewport({ scale: opts.scale || 1.5 });
    const canvas = document.createElement("canvas");
    canvas.width = viewport.width;
    canvas.height = viewport.height;
    canvas.style.cssText = "display:block;margin:0 auto 16px;box-shadow:0 2px 8px rgba(0,0,0,.1);max-width:100%";
    await page.render({ canvasContext: canvas.getContext("2d"), viewport }).promise;
    pagesDiv.appendChild(canvas);
  }
}

/* ─── Plain Text ─── */
async function renderText(source, target) {
  const { blob, url, arrayBuffer } = await resolveSource(source);
  const text = blob
    ? await blob.text()
    : new TextDecoder().decode(arrayBuffer);
  const preview = text.length > 50000 ? text.slice(0, 50000) + "\n\n... (truncated)" : text;
  target.innerHTML = `<pre class="fp-pre">${escapeHtml(preview)}</pre>`;
}

/* ─── CSV / TSV ─── */
async function renderCSV(source, target, ext) {
  const { blob, arrayBuffer } = await resolveSource(source);
  const text = blob ? await blob.text() : new TextDecoder().decode(arrayBuffer);
  const delimiter = ext === ".tsv" ? "\t" : ",";
  const result = Papa.parse(text, { delimiter, header: false });
  const raw = result.data;
  if (!raw || raw.length === 0) {
    target.innerHTML = `<div class="fp-placeholder">No data found</div>`;
    return;
  }
  renderCanvasGrid(target, raw, null);
}

/* ─── Excel (.xlsx / .xlsm / .xlsb) ─── */
async function renderExcel(source, target) {
  const { arrayBuffer } = await resolveSource(source);
  const workbook = XLSX.read(arrayBuffer, { type: "array", cellStyles: true, cellNF: true });
  const sheetNames = workbook.SheetNames;
  const sheetsData = sheetNames.map(name => parseSheet(workbook.Sheets[name]));
  const contentId = "cdg_" + Date.now() + "_" + Math.random().toString(36).slice(2, 7);

  let tabsHTML = '<div class="fp-sheet-tabs">';
  sheetNames.forEach((name, i) => {
    tabsHTML += `<span class="fp-sheet-tab ${i === 0 ? "active" : ""}" data-idx="${i}" data-cid="${contentId}">${escapeHtml(name)}</span>`;
  });
  tabsHTML += "</div>";

  let contentHTML = `<div id="${contentId}">`;
  sheetsData.forEach((_, i) => {
    contentHTML += `<div class="fp-sheet-content" style="display:${i === 0 ? "block" : "none"}"><div class="fp-cdg-container" id="${contentId}_g${i}"></div></div>`;
  });
  contentHTML += "</div>";

  target.innerHTML = tabsHTML + contentHTML;

  renderCanvasGrid(document.getElementById(`${contentId}_g0`), null, sheetsData[0]);

  /* keep per-tab cache so we don't re-render */
  window["_fp_sd_" + contentId] = sheetsData;
  window["_fp_sl_" + contentId] = new Set([0]);

  target.querySelectorAll(".fp-sheet-tab").forEach(tab => {
    tab.addEventListener("click", function () {
      const idx = parseInt(this.dataset.idx);
      const cid = this.dataset.cid;
      this.parentElement.querySelectorAll(".fp-sheet-tab").forEach(t => t.classList.remove("active"));
      this.classList.add("active");
      document.getElementById(cid).querySelectorAll(".fp-sheet-content").forEach((c, i) =>
        c.style.display = i === idx ? "block" : "none"
      );
      const loaded = window["_fp_sl_" + cid];
      if (!loaded.has(idx)) {
        renderCanvasGrid(document.getElementById(`${cid}_g${idx}`), null, window["_fp_sd_" + cid][idx]);
        loaded.add(idx);
      }
    });
  });
}

/* ─── DOCX → PDF pipeline ─── */
async function renderDocxAsPDF(source, target) {
  const { arrayBuffer } = await resolveSource(source);

  /* Step 1 — render docx into hidden HTML container */
  setStatus(target, "Step 1/3 — Rendering DOCX to HTML…");

  const hiddenContainer = document.getElementById("docxHiddenRender");
  hiddenContainer.style.visibility = "hidden";
  hiddenContainer.style.position = "absolute";
  hiddenContainer.style.left = "-9999px";
  hiddenContainer.style.width = "794px";
  hiddenContainer.innerHTML = "";

  await docx.renderAsync(arrayBuffer, hiddenContainer, null, {
    className: "docx",
    inWrapper: true,
    ignoreWidth: false,
    ignoreHeight: false,
    ignoreFonts: false,
    breakPages: true,
    renderHeaders: true,
    renderFooters: true,
    renderFootnotes: true,
    renderEndnotes: true,
    useBase64URL: true,
    experimental: true,
  });

  hiddenContainer.style.visibility = "visible";
  hiddenContainer.style.left = "-9999px";

  const pages = hiddenContainer.querySelectorAll("section.docx");
  pages.forEach(page => {
    page.style.background = "white";
    page.style.margin = "0";
    page.style.boxShadow = "none";
    page.style.overflow = "hidden";
  });

  await new Promise(r => setTimeout(r, 500));

  /* Step 2 — html2canvas each page → jsPDF */
  setStatus(target, `Step 2/3 — Converting ${pages.length} page(s) to PDF…`);

  const { jsPDF } = window.jspdf;
  const pdf = new jsPDF({ orientation: "portrait", unit: "pt", format: "a4" });
  const pdfW = pdf.internal.pageSize.getWidth();
  const pdfH = pdf.internal.pageSize.getHeight();

  for (let i = 0; i < pages.length; i++) {
    const page = pages[i];
    const canvas = await html2canvas(page, {
      scale: 2,
      useCORS: true,
      backgroundColor: "#ffffff",
      logging: false,
      windowWidth: 794,
    });
    const imgData = canvas.toDataURL("image/jpeg", 0.92);
    const imgW = pdfW;
    const imgH = (canvas.height * pdfW) / canvas.width;

    if (i > 0) pdf.addPage();

    if (imgH > pdfH) {
      const fitW = (canvas.width * pdfH) / canvas.height;
      pdf.addImage(imgData, "JPEG", (pdfW - fitW) / 2, 0, fitW, pdfH);
    } else {
      pdf.addImage(imgData, "JPEG", 0, 0, imgW, imgH);
    }
  }

  hiddenContainer.innerHTML = "";
  hiddenContainer.style.visibility = "hidden";

  /* Step 3 — render with PDF.js */
  setStatus(target, "Step 3/3 — Rendering PDF preview…");
  const pdfArrayBuffer = pdf.output("arraybuffer");
  await renderPDF(new Blob([pdfArrayBuffer], { type: "application/pdf" }), target);

  /* Pipeline note */
  const note = document.createElement("div");
  note.className = "fp-warning";
  note.style.cssText = "margin:12px 0 0";
  note.innerHTML = `<strong>Pipeline:</strong> DOCX → docx-preview → html2canvas → jsPDF (${pages.length} pages) → PDF.js<br>
    <span style="font-size:11px;opacity:.8">In production, Step 1-2 will run server-side via LibreOffice headless for 100% layout fidelity and faster conversion.</span>`;
  target.appendChild(note);
}

/* ─── Download Only (.doc / .xls) ─── */
async function renderDownloadOnly(source, target, ext, fileName) {
  const { blob } = await resolveSource(source);
  const url = blob ? URL.createObjectURL(blob) : source;
  const name = fileName || (typeof source === "string" ? source.split("/").pop() : "file");
  target.innerHTML = `
    <div class="fp-download-only">
      <div class="fp-dl-icon">📄</div>
      <div style="font-weight:600;font-size:14px;margin-bottom:6px">${ext} — Legacy Format</div>
      <p style="color:var(--muted,#64748b);font-size:13px;max-width:420px;text-align:center;line-height:1.5;margin:0 0 16px">
        Web preview is not available for this format (${ext === ".doc" ? "Word 97-2003" : "Excel 97-2003"}).<br>
        Please download to open with the desktop application.
      </p>
      <a class="fp-dl-btn" href="${url}" download="${escapeHtml(name)}">⬇ Download ${escapeHtml(name)}</a>
    </div>`;
}

/* ============================================================
   Main entry: render(target, { source, ext, name })
   ============================================================ */
async function render(target, { source, ext, name }) {
  ext = (ext || "").toLowerCase();
  if (!ext.startsWith(".")) ext = "." + ext;

  setStatus(target, "Loading preview…");

  try {
    if ([".png",".jpeg",".jpg"].includes(ext)) {
      await renderImage(source, target);
    } else if (ext === ".pdf") {
      await renderPDF(source, target);
    } else if ([".csv",".tsv"].includes(ext)) {
      await renderCSV(source, target, ext);
    } else if (ext === ".txt") {
      await renderText(source, target);
    } else if ([".xlsx",".xlsm",".xlsb"].includes(ext)) {
      await renderExcel(source, target);
    } else if (ext === ".docx") {
      await renderDocxAsPDF(source, target);
    } else if (DOWNLOAD_ONLY.includes(ext)) {
      await renderDownloadOnly(source, target, ext, name);
    } else {
      target.innerHTML = `<div class="fp-error">Unsupported file type: ${ext}</div>`;
    }
  } catch (err) {
    console.error("[FilePreview]", err);
    target.innerHTML = `<div class="fp-error">Error: ${escapeHtml(err.message || String(err))}</div>`;
  }
}

/* ============================================================
   Helpers
   ============================================================ */

function setStatus(target, msg) {
  target.innerHTML = `<div class="fp-placeholder"><div class="fp-spinner"></div><div style="margin-top:12px;color:var(--muted,#64748b);font-size:13px">${escapeHtml(msg)}</div></div>`;
}

function escapeHtml(str) {
  if (str == null) return "";
  const div = document.createElement("div");
  div.textContent = String(str);
  return div.innerHTML;
}

function colLetter(index) {
  let letter = "";
  let n = index;
  while (n >= 0) {
    letter = String.fromCharCode((n % 26) + 65) + letter;
    n = Math.floor(n / 26) - 1;
  }
  return letter;
}

function parseColor(color) {
  if (!color) return null;
  if (color.rgb && color.rgb !== "000000") return "#" + color.rgb;
  if (color.theme != null) {
    const themeColors = ["#FFFFFF","#000000","#44546A","#4472C4","#ED7D31","#A5A5A5","#FFC000","#5B9BD5","#70AD47","#7030A0","#FF0000","#00B050"];
    return themeColors[color.theme] || null;
  }
  return null;
}

function applyTint(hex, tint) {
  if (!hex || !tint) return hex;
  const r = parseInt(hex.slice(1,3),16);
  const g = parseInt(hex.slice(3,5),16);
  const b = parseInt(hex.slice(5,7),16);
  const t = parseFloat(tint);
  const apply = c => t > 0 ? Math.round(c + (255 - c) * t) : t < 0 ? Math.round(c * (1 + t)) : c;
  return "#" + [apply(r), apply(g), apply(b)]
    .map(x => Math.min(255, Math.max(0, x)).toString(16).padStart(2, "0"))
    .join("");
}

function parseSheet(ws) {
  const ref = ws["!ref"];
  if (!ref) return { data: [], formats: {}, colWidths: {} };

  const range = XLSX.utils.decode_range(ref);
  const maxCols = range.e.c - range.s.c + 1;
  const colKeys = [];
  for (let c = 0; c < maxCols; c++) colKeys.push(colLetter(c));

  const data = [];
  const formats = {};

  for (let r = range.s.r; r <= range.e.r; r++) {
    const rowObj = {};
    for (let c = range.s.c; c <= range.e.c; c++) {
      const key = colKeys[c - range.s.c];
      const cell = ws[XLSX.utils.encode_cell({ r, c })];

      if (cell) {
        rowObj[key] = cell.w != null ? cell.w : cell.v != null ? cell.v : "";
        if (cell.s) {
          const s = cell.s;
          const fmt = {};
          if (s.fill) {
            let bg = parseColor(s.fill.fgColor);
            if (bg && s.fill.fgColor && s.fill.fgColor.tint) bg = applyTint(bg, s.fill.fgColor.tint);
            if (bg && bg !== "#FFFFFF" && bg !== "#ffffff") fmt.bg = bg;
          }
          if (s.fgColor) {
            const bg = parseColor(s.fgColor);
            if (bg && bg !== "#FFFFFF") fmt.bg = bg;
          }
          if (s.font) {
            if (s.font.bold) fmt.bold = true;
            if (s.font.italic) fmt.italic = true;
            if (s.font.sz) fmt.fontSize = s.font.sz;
            const fc = parseColor(s.font.color);
            if (fc && fc !== "#000000") fmt.fg = fc;
          }
          if (s.alignment && s.alignment.horizontal) fmt.align = s.alignment.horizontal;
          if (Object.keys(fmt).length > 0) formats[(r - range.s.r) + ":" + (c - range.s.c)] = fmt;
        }
      } else {
        rowObj[key] = "";
      }
    }
    data.push(rowObj);
  }

  const colWidths = {};
  if (ws["!cols"]) {
    ws["!cols"].forEach((col, i) => {
      if (col && colKeys[i]) {
        if (col.wpx) colWidths[colKeys[i]] = col.wpx;
        else if (col.wch) colWidths[colKeys[i]] = Math.round(col.wch * 7.5);
      }
    });
  }

  return { data, formats, colWidths };
}

function renderCanvasGrid(container, rawData, sheetInfo) {
  if (!container.classList.contains("fp-cdg-container")) {
    container.innerHTML = '<div class="fp-cdg-container"></div>';
    container = container.querySelector(".fp-cdg-container");
  }

  let data, formats = {}, colWidths = {};
  if (sheetInfo) {
    data = sheetInfo.data;
    formats = sheetInfo.formats || {};
    colWidths = sheetInfo.colWidths || {};
  } else if (rawData) {
    const maxCols = rawData.reduce((max, row) => Math.max(max, row.length), 0);
    const colKeys = [];
    for (let c = 0; c < maxCols; c++) colKeys.push(colLetter(c));
    data = rawData.map(row => {
      const obj = {};
      for (let c = 0; c < maxCols; c++) obj[colKeys[c]] = row[c] !== undefined ? row[c] : "";
      return obj;
    });
  } else {
    container.innerHTML = '<div class="fp-placeholder">No data found</div>';
    return;
  }

  if (!data || data.length === 0) {
    container.innerHTML = '<div class="fp-placeholder">No data found</div>';
    return;
  }

  const grid = canvasDatagrid();
  container.appendChild(grid);
  grid.data = data;
  grid.attributes.editable = false;

  if (grid.schema) {
    grid.schema.forEach(col => {
      if (colWidths[col.name]) col.width = colWidths[col.name];
    });
  }

  const hasFormats = Object.keys(formats).length > 0;
  if (hasFormats) {
    grid.addEventListener("afterrendercell", function (e) {
      const ri = e.cell.rowIndex;
      const ci = e.cell.columnIndex;
      if (ri < 0 || e.cell.isHeader || e.cell.isColumnHeader || e.cell.isRowHeader || e.cell.isCorner) return;
      const fmt = formats[ri + ":" + ci];
      if (!fmt || !fmt.bg) return;
      e.ctx.save();
      e.ctx.fillStyle = fmt.bg;
      e.ctx.globalCompositeOperation = "destination-over";
      e.ctx.fillRect(e.cell.x, e.cell.y, e.cell.width, e.cell.height);
      e.ctx.restore();
    });

    grid.addEventListener("rendertext", function (e) {
      const ri = e.cell.rowIndex;
      const ci = e.cell.columnIndex;
      if (ri < 0 || e.cell.isHeader || e.cell.isColumnHeader || e.cell.isRowHeader || e.cell.isCorner) return;
      const fmt = formats[ri + ":" + ci];
      if (!fmt) return;
      if (fmt.fg) e.ctx.fillStyle = fmt.fg;
      if (fmt.bold || fmt.italic || fmt.fontSize) {
        const parts = [];
        if (fmt.bold) parts.push("bold");
        if (fmt.italic) parts.push("italic");
        parts.push((fmt.fontSize || 12) + "px", "sans-serif");
        e.ctx.font = parts.join(" ");
      }
    });
  }

  grid.style.width = "100%";
  grid.style.height = "100%";
}

/* expose */
window.FilePreview = {
  render,
  methodOf,
  PREVIEW_SUPPORTED,
  DOWNLOAD_ONLY,
};
