import { registerFeature } from '../registry';
import { featureModal, pdfSource, footerButtons, type LoadedFile } from '../common';
import { baseName, parsePageRanges, download } from '../../core/util';
import { loadPdf, renderPageToBlob } from '../../core/pdf';
import { makeZip } from '../../core/zip';
import { celebrateExport, toast } from '../../ui/toast';
import { field, segmented, select, textInput } from '../../ui/controls';

type Fmt = 'png' | 'jpeg' | 'webp';
const MIME: Record<Fmt, 'image/png' | 'image/jpeg' | 'image/webp'> = {
  png: 'image/png',
  jpeg: 'image/jpeg',
  webp: 'image/webp',
};
const EXT: Record<Fmt, string> = { png: 'png', jpeg: 'jpg', webp: 'webp' };

registerFeature({
  id: 'pdf-to-images',
  title: 'PDF to images',
  desc: 'Export each page as a PNG, JPG or WebP.',
  icon: 'image',
  category: 'convert-out',
  run(ctx) {
    let file: LoadedFile | null = null;
    let format: Fmt = 'png';
    let scale = 2;
    let mode: 'all' | 'custom' = 'all';
    let ranges = '';

    const m = featureModal('PDF to images', 'Render pages to images — nothing leaves your device.', 'image', 560);

    const rangeInput = textInput({
      placeholder: 'e.g. 1-3, 5, 8-10',
      oninput: (v) => { ranges = v; },
    });
    const rangeField = field('Pages', rangeInput, 'Page numbers to export.');
    rangeField.style.display = 'none';

    const pagesSeg = segmented({
      options: [{ value: 'all', label: 'All pages' }, { value: 'custom', label: 'Custom' }],
      value: 'all',
      onchange: (v) => {
        mode = v === 'custom' ? 'custom' : 'all';
        rangeField.style.display = mode === 'custom' ? '' : 'none';
      },
    });

    m.body.append(
      pdfSource(ctx, { multiple: false, onChange: (f) => { file = f[0] ?? null; primary.disabled = !file; } }),
      field('Format', segmented({
        options: [
          { value: 'png', label: 'PNG' },
          { value: 'jpeg', label: 'JPEG' },
          { value: 'webp', label: 'WebP' },
        ],
        value: 'png',
        onchange: (v) => { format = v as Fmt; },
      })),
      field('Resolution', select({
        options: [
          { value: '1', label: '1x — 72 dpi' },
          { value: '2', label: '2x — 144 dpi' },
          { value: '3', label: '3x — 216 dpi' },
        ],
        value: '2',
        onchange: (v) => { scale = parseInt(v, 10) || 2; },
      })),
      field('Which pages', pagesSeg),
      rangeField,
    );

    const { primary } = footerButtons(m, 'Render & download', async () => {
      if (!file) return;
      const current = file;
      const mime = MIME[format];
      const ext = EXT[format];
      const t = toast('Reading PDF…', { type: 'progress' });
      try {
        const doc = await loadPdf(current.bytes);
        const total = doc.numPages;
        let indices: number[];
        if (mode === 'custom') {
          indices = parsePageRanges(ranges, total);
          if (indices.length === 0) {
            t.close();
            toast('No valid pages in that range.', { type: 'error' });
            return;
          }
        } else {
          indices = Array.from({ length: total }, (_, i) => i);
        }

        const base = baseName(current.name);
        const files: { name: string; data: Uint8Array }[] = [];
        for (let k = 0; k < indices.length; k++) {
          const i = indices[k];
          t.update(`Rendering page ${k + 1} of ${indices.length}…`);
          t.progress((k) / indices.length);
          const page = await doc.getPage(i + 1);
          const blob = await renderPageToBlob(page, scale, mime, 0.92);
          files.push({ name: `${base}-p${i + 1}.${ext}`, data: new Uint8Array(await blob.arrayBuffer()) });
        }
        t.progress(1);

        m.close();
        if (files.length === 1) {
          const only = files[0];
          download(only.data, only.name, mime);
          t.close();
          celebrateExport(only.name);
        } else {
          const zip = makeZip(files);
          const zipName = `${base}-images.zip`;
          download(zip, zipName);
          t.close();
          celebrateExport(zipName);
        }
      } catch (e) {
        t.close();
        toast('Could not render this PDF — it may be encrypted or damaged.', { type: 'error' });
        console.error(e);
      }
    }, 'image');
    primary.disabled = true;
  },
});
