/* ════════════════════════════════════════════════════════════════════
   tab-pdf-to-image.js — conversion d'un PDF en images PNG ou JPG.
   Chaque page est rendue par PDF.js sur un canvas à la résolution
   choisie, encodée par le navigateur, puis proposée au téléchargement
   à l'unité ou groupée dans une archive ZIP (JSZip, en local).
   ════════════════════════════════════════════════════════════════════ */
(() => {
  'use strict';
  const { byId, esc, toast, readFile, downloadBlob, baseName, formatSize, padNum } = Utils;

  const state = {
    /** @type {{name:string, doc:any}|null} */
    file: null,
    /** @type {{blob:Blob, url:string, name:string, w:number, h:number}[]} */
    results: [],
    busy: false,
  };

  const els = {};
  let prog;

  function init() {
    ['drop', 'file', 'workspace', 'card', 'quality', 'quality-wrap', 'quality-val',
     'dpi', 'convert', 'reset', 'progress', 'results', 'results-bar', 'results-count', 'zip']
      .forEach((k) => { els[k.replace(/-(\w)/g, (_, c) => c.toUpperCase())] = byId(`p2i-${k}`); });
    prog = Utils.progress(els.progress);

    Utils.setupDropzone(els.drop, els.file, (files) => onFile(files[0]));
    els.convert.addEventListener('click', convert);
    els.zip.addEventListener('click', downloadZip);
    els.reset.addEventListener('click', resetAll);

    // La qualité ne concerne que le JPG
    document.querySelectorAll('input[name="p2i-format"]').forEach((r) =>
      r.addEventListener('change', () => {
        els.qualityWrap.hidden = getFormat() !== 'jpeg';
      }));
    els.quality.addEventListener('input', () => {
      els.qualityVal.textContent = `${els.quality.value}\u00a0%`;
    });

    App.registerTab('pdf2img', () => state.file !== null);
  }

  const getFormat = () =>
    document.querySelector('input[name="p2i-format"]:checked').value;

  async function onFile(file) {
    if (!Utils.isPdf(file)) { toast('Déposez un fichier PDF.', 'error'); return; }
    try {
      const bytes = await readFile(file);
      const opened = await PdfEngine.openPdfJsInteractive(bytes, file.name);
      if (!opened) return; // mot de passe non fourni
      if (state.file) state.file.doc.destroy();
      clearResults();
      state.file = { name: file.name, doc: opened.doc };
      els.card.innerHTML =
        `<svg class="icon"><use href="#i-file"/></svg>` +
        `<span class="row-body"><span class="row-name">${esc(file.name)}</span>` +
        `<span class="row-meta">${opened.doc.numPages} page${opened.doc.numPages > 1 ? 's' : ''} · ${formatSize(file.size)}</span></span>`;
      els.drop.hidden = true;
      els.workspace.hidden = false;
    } catch (e) {
      console.error(e);
      toast(`« ${file.name} » : ${e.message}`, 'error');
    }
  }

  /** canvas.toBlob sous forme de promesse. */
  function canvasToBlob(canvas, mime, quality) {
    return new Promise((resolve, reject) => {
      canvas.toBlob(
        (blob) => blob ? resolve(blob) : reject(new Error('Encodage de l\u2019image impossible.')),
        mime, quality,
      );
    });
  }

  async function convert() {
    if (!state.file || state.busy) return;
    const doc = state.file.doc;
    const format = getFormat();                    // 'png' | 'jpeg'
    const mime = `image/${format}`;
    const ext = format === 'jpeg' ? 'jpg' : 'png';
    const quality = format === 'jpeg' ? Number(els.quality.value) / 100 : undefined;
    const scale = Number(els.dpi.value) / 72;      // l'unité PDF vaut 1/72 de pouce
    const base = baseName(state.file.name);
    const total = doc.numPages;

    setBusy(true);
    clearResults();
    prog.start(`0 / ${total}`);
    try {
      for (let i = 0; i < total; i++) {
        prog.set(i, total, `Page ${i + 1} / ${total}`);
        let canvas = await PdfEngine.renderFull(doc, i, scale);
        const blob = await canvasToBlob(canvas, mime, quality);
        const item = {
          blob,
          url: URL.createObjectURL(blob),
          name: `${base}-p${padNum(i + 1, total)}.${ext}`,
          w: canvas.width,
          h: canvas.height,
        };
        canvas.width = canvas.height = 0; // libère la mémoire du canvas
        canvas = null;
        state.results.push(item);
        els.results.appendChild(resultEl(item)); // affichage au fil de l'eau
      }
      prog.set(total, total, 'Terminé');
      els.resultsBar.hidden = false;
      els.resultsCount.textContent =
        `${total} image${total > 1 ? 's' : ''} générée${total > 1 ? 's' : ''} — ` +
        formatSize(state.results.reduce((n, r) => n + r.blob.size, 0));
      toast(`${total} image${total > 1 ? 's' : ''} prête${total > 1 ? 's' : ''}.`, 'success');
    } catch (e) {
      console.error(e);
      toast(`Échec de la conversion : ${e.message}`, 'error');
    } finally {
      prog.done();
      setBusy(false);
    }
  }

  function resultEl(item) {
    const card = document.createElement('div');
    card.className = 'result-card';
    card.innerHTML =
      `<img src="${item.url}" alt="${esc(item.name)}" loading="lazy">` +
      `<span class="result-name">${esc(item.name)}</span>` +
      `<span class="result-meta">${item.w} × ${item.h} px · ${formatSize(item.blob.size)}</span>` +
      `<button type="button" class="btn"><svg class="icon"><use href="#i-download"/></svg>Télécharger</button>`;
    card.querySelector('button').addEventListener('click', () =>
      downloadBlob(item.blob, item.name));
    return card;
  }

  async function downloadZip() {
    if (!state.results.length || state.busy) return;
    setBusy(true);
    prog.start('Création de l\u2019archive…', true);
    try {
      const zip = new JSZip();
      for (const r of state.results) zip.file(r.name, r.blob);
      // STORE : les PNG/JPG sont déjà compressés, inutile de recompresser
      const blob = await zip.generateAsync(
        { type: 'blob', compression: 'STORE' },
        (meta) => prog.set(Math.round(meta.percent), 100, `${Math.round(meta.percent)} %`),
      );
      const name = `${baseName(state.file.name)}-images.zip`;
      downloadBlob(blob, name, 'application/zip');
      toast(`« ${name} » téléchargé (${formatSize(blob.size)}).`, 'success');
    } catch (e) {
      console.error(e);
      toast(`Échec de la création du ZIP : ${e.message}`, 'error');
    } finally {
      prog.done();
      setBusy(false);
    }
  }

  function setBusy(b) {
    state.busy = b;
    els.convert.disabled = b;
    els.zip.disabled = b;
    els.reset.disabled = b;
  }

  function clearResults() {
    for (const r of state.results) URL.revokeObjectURL(r.url);
    state.results = [];
    els.results.replaceChildren();
    els.resultsBar.hidden = true;
  }

  function resetAll() {
    if (state.file) state.file.doc.destroy();
    state.file = null;
    clearResults();
    els.workspace.hidden = true;
    els.drop.hidden = false;
  }

  document.addEventListener('DOMContentLoaded', init);
})();
