import { PDFDocument } from '@cantoo/pdf-lib';
import { registerFeature } from '../registry';
import { featureModal, pdfSource, footerButtons, deliver, type LoadedFile } from '../common';
import { field, textInput, numberInput, segmented } from '../../ui/controls';
import { bufferOf, baseName, parsePageRanges, download } from '../../core/util';
import { makeZip } from '../../core/zip';
import { loadPdf } from '../../core/pdf';
import { toast, celebrateExport } from '../../ui/toast';
import { h } from '../../core/dom';

type SplitMode = 'ranges' | 'every' | 'each';

registerFeature({
  id: 'split',
  title: 'Split PDF',
  desc: 'Break one PDF into several files.',
  icon: 'split',
  category: 'organize',
  run(ctx) {
    let file: LoadedFile | null = null;
    let numPages = 0;
    let mode: SplitMode = 'ranges';
    let rangesSpec = '';
    let everyN = 1;

    const m = featureModal('Split PDF', 'Break one PDF into several — nothing is uploaded.', 'split', 560);

    const pageInfo = h('p.hint', 'Choose a PDF to begin.');

    const rangesInput = textInput({
      placeholder: 'e.g. 1-3, 4-8, 9',
      oninput: (v) => { rangesSpec = v; },
    });
    const everyInput = numberInput({
      value: 1,
      min: 1,
      step: 1,
      onchange: (v) => { everyN = Math.max(1, Math.floor(v || 1)); },
    });

    const rangesField = field('Page ranges', rangesInput, 'Each comma-separated group becomes one output file.');
    const everyField = field('Split every N pages', everyInput, 'Each output file gets this many consecutive pages.');
    const eachHint = h('p.hint', 'Every page becomes its own PDF.');

    const optionsWrap = h('div');
    const renderOptions = () => {
      optionsWrap.replaceChildren(
        mode === 'ranges' ? rangesField : mode === 'every' ? everyField : eachHint,
      );
    };
    renderOptions();

    const modeControl = segmented({
      options: [
        { value: 'ranges', label: 'Ranges' },
        { value: 'every', label: 'Every N' },
        { value: 'each', label: 'Each page' },
      ],
      value: mode,
      onchange: (v) => { mode = v as SplitMode; renderOptions(); },
    });

    m.body.append(
      pdfSource(ctx, {
        multiple: false,
        onChange: async (files) => {
          file = files[0] ?? null;
          numPages = 0;
          if (!file) {
            pageInfo.textContent = 'Choose a PDF to begin.';
            primary.disabled = true;
            return;
          }
          pageInfo.textContent = 'Reading…';
          primary.disabled = true;
          try {
            const doc = await loadPdf(file.bytes);
            numPages = doc.numPages;
            pageInfo.textContent = `${numPages} page${numPages === 1 ? '' : 's'}`;
            primary.disabled = false;
          } catch (e) {
            console.error(e);
            pageInfo.textContent = 'Could not read that PDF — it may be damaged.';
            file = null;
          }
        },
      }),
      pageInfo,
      field('Split mode', modeControl),
      optionsWrap,
    );

    const { primary } = footerButtons(m, 'Split & download', async () => {
      if (!file || numPages < 1) return;

      // Compute the list of page-index groups for each output file.
      let groups: number[][] = [];
      if (mode === 'ranges') {
        for (const part of rangesSpec.split(',')) {
          const seg = part.trim();
          if (!seg) continue;
          const idx = parsePageRanges(seg, numPages);
          if (idx.length) groups.push(idx);
        }
        if (!groups.length) {
          toast('Enter at least one valid page range.', { type: 'error' });
          return;
        }
      } else if (mode === 'every') {
        const n = Math.max(1, Math.floor(everyN || 1));
        for (let start = 0; start < numPages; start += n) {
          const idx: number[] = [];
          for (let i = start; i < Math.min(start + n, numPages); i++) idx.push(i);
          groups.push(idx);
        }
      } else {
        for (let i = 0; i < numPages; i++) groups.push([i]);
      }

      const t = toast('Splitting…', { type: 'progress' });
      m.setBusy(true, 'Splitting…');
      try {
        const src = await PDFDocument.load(bufferOf(file.bytes), { ignoreEncryption: true });
        const outputs: { name: string; data: Uint8Array }[] = [];
        const pad = String(groups.length).length;
        for (let g = 0; g < groups.length; g++) {
          t.update(`Building part ${g + 1} of ${groups.length}…`);
          t.progress(g / groups.length);
          const out = await PDFDocument.create();
          const copied = await out.copyPages(src, groups[g]);
          copied.forEach((p) => out.addPage(p));
          const b = await out.save({ useObjectStreams: true });
          outputs.push({ name: `part-${String(g + 1).padStart(pad, '0')}.pdf`, data: b });
        }
        t.progress(1);
        t.close();
        m.close();

        if (outputs.length === 1) {
          deliver(outputs[0].data, `${baseName(file.name)}-split.pdf`);
        } else {
          const zip = makeZip(outputs);
          const zipName = `${baseName(file.name)}-split.zip`;
          download(zip, zipName, 'application/zip');
          celebrateExport(zipName);
        }
      } catch (e) {
        t.close();
        m.setBusy(false);
        toast('Split failed — the PDF may be damaged.', { type: 'error' });
        console.error(e);
      }
    }, 'split');
    primary.disabled = true;
  },
});
