// Shared batch-mode helper. A tool passes a `process(file, opts) -> Blob`
// function and we handle: multi-file dropzone, per-file progress, errors,
// and packaging every output as a single .zip via fflate (CDN).

const FFLATE_URL = 'https://cdn.jsdelivr.net/npm/fflate@0.8.2/umd/index.js';

window.BatchMode = function BatchMode({
  onExit,                   // callback to return to single-file mode
  process,                  // (file, opts) => Promise<{ blob, ext } | Blob>
  opts,                     // forwarded as second arg to process()
  accept = 'image/*',
  title = 'Drop files here',
  hint = 'multiple files at once',
  defaultExt = 'bin',
  zipName = 'batch',
  renderControls,           // optional ({ opts, setOpts }) => JSX for per-tool options
  setOpts,
}) {
  const [files, setFiles] = React.useState([]);
  const [results, setResults] = React.useState([]);  // { name, size, blob, ext, error }
  const [running, setRunning] = React.useState(false);
  const [done, setDone] = React.useState(0);

  const add = (picked) => {
    const list = Array.isArray(picked) ? picked : [picked];
    setFiles((prev) => [...prev, ...list]);
  };
  const removeAt = (i) => setFiles((p) => p.filter((_, idx) => idx !== i));
  const clear = () => { setFiles([]); setResults([]); setDone(0); };

  const run = async () => {
    setRunning(true); setResults([]); setDone(0);
    const out = [];
    for (let i = 0; i < files.length; i++) {
      const f = files[i];
      try {
        const r = await process(f, opts);
        const blob = r instanceof Blob ? r : r.blob;
        const ext = (r && r.ext) || defaultExt;
        out.push({ name: f.name, size: blob.size, blob, ext });
      } catch (e) {
        out.push({ name: f.name, error: e.message || String(e) });
      }
      setDone(i + 1);
      setResults([...out]);
      // Yield so the UI can repaint.
      await new Promise((r) => requestAnimationFrame(r));
    }
    setRunning(false);
  };

  const downloadZip = async () => {
    await window.loadScript(FFLATE_URL);
    const zip = {};
    for (const r of results) {
      if (!r.blob) continue;
      const base = r.name.replace(/\.[^.]+$/, '');
      const key = `${base}.${r.ext}`;
      zip[key] = new Uint8Array(await r.blob.arrayBuffer());
    }
    window.fflate.zip(zip, (err, data) => {
      if (err) return;
      window.downloadBlob(new Blob([data], { type: 'application/zip' }), `${zipName}.zip`);
    });
  };

  const downloadOne = (r) => {
    if (!r.blob) return;
    const name = r.name.replace(/\.[^.]+$/, '') + '.' + r.ext;
    window.downloadBlob(r.blob, name);
  };

  const totalSize = files.reduce((s, f) => s + f.size, 0);
  const okResults = results.filter((r) => r.blob);
  const outSize = okResults.reduce((s, r) => s + r.size, 0);

  return (
    <div className="mini-tool">
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
        <span className="filter-pill active" style={{ pointerEvents: 'none' }}>
          <window.Icon name="grid" size={12} /> Batch mode
        </span>
        {onExit && <button className="filter-pill" onClick={onExit}>← Single file</button>}
        <span className="cmp-meta" style={{ marginLeft: 'auto' }}>
          {files.length} file{files.length === 1 ? '' : 's'} · {window.fmtBytes(totalSize)}
        </span>
      </div>

      {renderControls && setOpts && renderControls({ opts, setOpts })}

      {files.length === 0 ? (
        <window.Dropzone onFile={add} multiple title={title} hint={hint} accept={accept} />
      ) : (
        <>
          <div style={{ maxHeight: 260, overflowY: 'auto', border: '1px solid var(--id-border)', borderRadius: 10, marginTop: renderControls ? 12 : 0 }}>
            {files.map((f, i) => {
              const r = results[i];
              return (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', borderBottom: '1px solid var(--id-border)', fontSize: 13 }}>
                  <window.Icon name={r?.error ? 'x' : r?.blob ? 'check' : 'doc'} size={14}
                               style={{ color: r?.error ? '#c8321f' : r?.blob ? 'var(--id-success)' : 'var(--id-text-muted)' }} />
                  <div style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{f.name}</div>
                  <span className="cmp-meta">
                    {r?.blob ? `${window.fmtBytes(r.size)}` : r?.error ? r.error.slice(0, 30) : window.fmtBytes(f.size)}
                  </span>
                  {r?.blob && <button className="icon-btn" onClick={() => downloadOne(r)} title="Download"><window.Icon name="download" size={12} /></button>}
                  {!running && <button className="icon-btn" onClick={() => removeAt(i)}><window.Icon name="x" size={12} /></button>}
                </div>
              );
            })}
          </div>

          {running && (
            <div style={{ marginTop: 12 }}>
              <div className="pw-bar"><div className="pw-fill" style={{ width: `${(done / files.length) * 100}%`, background: 'var(--id-brand-blue)' }} /></div>
              <div className="cmp-meta" style={{ textAlign: 'center', marginTop: 6 }}>{done} / {files.length}</div>
            </div>
          )}

          {okResults.length > 0 && !running && (
            <div className="cmp-stats" style={{ marginTop: 14 }}>
              <div className="cmp-stat success"><div className="sv">{okResults.length}</div><div className="sl">Processed</div></div>
              <div className="cmp-stat"><div className="sv">{window.fmtBytes(outSize)}</div><div className="sl">Total output</div></div>
              <div className="cmp-stat"><div className="sv">{results.length - okResults.length}</div><div className="sl">Failed</div></div>
            </div>
          )}
        </>
      )}

      <div className="cmp-actions">
        {files.length > 0 && <button className="btn btn-secondary" onClick={clear} disabled={running}><window.Icon name="x" size={16} /> Clear</button>}
        <label className="btn btn-secondary">
          <window.Icon name="upload" size={16} /> Add more
          <input type="file" accept={accept} multiple hidden onChange={(e) => add(Array.from(e.target.files))} />
        </label>
        {okResults.length === files.length && files.length > 0 && !running ? (
          <button className="btn btn-primary" onClick={downloadZip}>
            <window.Icon name="archive" size={16} /> Download all as ZIP
          </button>
        ) : (
          <button className="btn btn-primary" onClick={run} disabled={running || files.length === 0}>
            <window.Icon name="bolt" size={16} /> {running ? `Processing… ${done}/${files.length}` : `Process ${files.length} file${files.length === 1 ? '' : 's'}`}
          </button>
        )}
      </div>
    </div>
  );
};
