/* ═══════════════════════════════════════════════════════════════════
   ImageBgRemoveWizard.jsx — Background Remover (Increment 3)
   First production consumer of the WizardShell (Increment 2). Stepped flow:
     1) Upload images  2) Options  3) Process + results
   Wired to the live Image Tools API (v1.28.0+ / v1.31 stream-proxy):
     GET  /images/allowance              — remaining allowance
     POST /images/uploads                — signed upload URLs (PUT bytes)
     POST /images/remove-bg              — enqueue bg_remove op
     GET  /images/batch/:id              — poll status
     GET  /images/file/:file_id          — stream cut-out PNG (auth blob)
   Output PNGs are streamed through the API proxy (never a raw signed URL).
   Auth + refresh handled by window.WPSB_Auth.authedFetch (reused, not rebuilt).
   image.bg_removed analytics events are emitted server-side on completion.

   Pattern: IIFE, React.createElement (pure ASCII), window.WPSB.* + back-compat.
   ═══════════════════════════════════════════════════════════════════ */
(function () {
  const { useState, useEffect, useRef, useCallback } = React;
  const h = React.createElement;
  const tx = (k, v, f) => (window.WPSB && window.WPSB.t) ? window.WPSB.t(k, v, f) : (f != null ? f : k);

  // Wizard primitives (Increment 2)
  const WizardShell = window.WizardShell;
  const ModeSelectorGroup = window.ModeSelectorGroup;
  const NoticeCard = window.NoticeCard;
  const WIcon = window.WIcon;

  /* ── CONFIG / API CLIENT (mirrors FileManager.jsx) ───────────────── */
  const RAILWAY_URL = (window.WPSB_CONFIG && window.WPSB_CONFIG.RAILWAY_URL)
    || 'https://wpsitebeam-railway-api-production.up.railway.app';

  function getFallbackToken() {
    return (window.WPSB && window.WPSB.getToken && window.WPSB.getToken())
      || localStorage.getItem('wpsb-auth-token') || null;
  }
  async function api(path, opts) {
    opts = opts || {};
    const init = { method: opts.method || 'GET', headers: {} };
    if (opts.body !== undefined) { init.headers['Content-Type'] = 'application/json'; init.body = JSON.stringify(opts.body); }
    let resp;
    try {
      if (window.WPSB_Auth && typeof window.WPSB_Auth.authedFetch === 'function') {
        resp = await window.WPSB_Auth.authedFetch(RAILWAY_URL + path, init);
      } else {
        const token = getFallbackToken();
        if (token) init.headers['Authorization'] = 'Bearer ' + token;
        resp = await fetch(RAILWAY_URL + path, init);
      }
    } catch (_) { return { ok: false, status: 0, code: 'NETWORK', error: tx('page.bgremove.err_network', null, 'Network error - could not reach the server.') }; }
    let data = null;
    try { data = await resp.json(); } catch (_) { data = null; }
    if (!resp.ok) return { ok: false, status: resp.status, code: (data && data.code) || 'ERROR', error: (data && data.error) || tx('page.bgremove.err_request_failed', { status: resp.status }, 'Request failed ({status})'), data };
    return { ok: true, status: resp.status, data: data || {} };
  }
  // Authed GET -> Blob (stream-through proxy; no signed URL ever reaches the client).
  async function apiBlob(path) {
    const init = { method: 'GET', headers: {} };
    let resp;
    try {
      if (window.WPSB_Auth && typeof window.WPSB_Auth.authedFetch === 'function') {
        resp = await window.WPSB_Auth.authedFetch(RAILWAY_URL + path, init);
      } else {
        const token = getFallbackToken();
        if (token) init.headers['Authorization'] = 'Bearer ' + token;
        resp = await fetch(RAILWAY_URL + path, init);
      }
    } catch (_) { return { ok: false, error: tx('page.bgremove.err_network', null, 'Network error - could not reach the server.') }; }
    if (!resp.ok) { let msg = tx('page.bgremove.err_download_failed_status', { status: resp.status }, 'Download failed ({status})'); try { const j = await resp.json(); if (j && j.error) msg = j.error; } catch (_) {} return { ok: false, status: resp.status, error: msg }; }
    const blob = await resp.blob();
    return { ok: true, blob };
  }

  const RATIO_OPTIONS = [
    { key: 'original', label: tx('page.bgremove.ratio_original', null, 'Original'), qualifier: tx('page.bgremove.ratio_original_qual', null, 'Keep source size'), detail: tx('page.bgremove.ratio_original_detail', null, 'No crop - same dimensions as the upload.') },
    { key: '1:1',      label: tx('page.bgremove.ratio_square', null, 'Square'),   qualifier: '1:1', detail: tx('page.bgremove.ratio_square_detail', null, 'Profile pics, product tiles, avatars.') },
    { key: '16:9',     label: tx('page.bgremove.ratio_landscape', null, 'Landscape'),qualifier: '16:9', detail: tx('page.bgremove.ratio_landscape_detail', null, 'Hero banners, slide backgrounds.') },
    { key: '9:16',     label: tx('page.bgremove.ratio_portrait', null, 'Portrait'), qualifier: '9:16', detail: tx('page.bgremove.ratio_portrait_detail', null, 'Stories, reels, mobile.') },
  ];
  const IMG_EXT = /\.(png|jpe?g|webp|gif|bmp|tiff?)$/i;

  /* ── Component ───────────────────────────────────────────────────── */
  function ImageBgRemoveWizard() {
    const [step, setStep] = useState(0);
    const [maxReached, setMaxReached] = useState(0);
    const [files, setFiles] = useState([]);            // File[]
    const [ratio, setRatio] = useState('original');
    const [busy, setBusy] = useState(false);
    const [error, setError] = useState(null);
    const [batchId, setBatchId] = useState(null);
    const [opId, setOpId] = useState(null);
    const [status, setStatus] = useState(null);        // batch poll result
    const [results, setResults] = useState([]);        // [{file_id, source_name, status, previewUrl}]
    const [allowance, setAllowance] = useState(null);
    const fileInputRef = useRef(null);
    const pollRef = useRef(null);
    const objectUrls = useRef([]);

    const STEPS = [
      { key: 'upload',  title: tx('page.bgremove.step_upload_title', null, 'Upload images'), subtitle: tx('page.bgremove.step_upload_subtitle', null, 'Pick the files') },
      { key: 'options', title: tx('page.bgremove.step_options_title', null, 'Options'),       subtitle: tx('page.bgremove.output_crop', null, 'Output crop') },
      { key: 'process', title: tx('page.bgremove.step_process_title', null, 'Remove + download'), subtitle: tx('page.bgremove.step_process_subtitle', null, 'Processing') },
    ];

    const loadAllowance = useCallback(async () => {
      const r = await api('/images/allowance');
      if (r.ok) setAllowance(r.data);
    }, []);
    useEffect(() => { loadAllowance(); }, [loadAllowance]);

    // cleanup object URLs on unmount
    useEffect(() => () => { objectUrls.current.forEach(u => { try { URL.revokeObjectURL(u); } catch (_) {} }); if (pollRef.current) clearInterval(pollRef.current); }, []);

    function onPickFiles(e) {
      setError(null);
      const picked = Array.from(e.target.files || []).filter(f => IMG_EXT.test(f.name));
      const rejected = Array.from(e.target.files || []).length - picked.length;
      if (rejected > 0) setError(tx('page.bgremove.files_skipped', { n: rejected }, '{n} file(s) skipped - only PNG, JPG, WEBP, GIF, BMP, TIFF are supported.'));
      setFiles(prev => {
        const seen = new Set(prev.map(f => f.name + ':' + f.size));
        return prev.concat(picked.filter(f => !seen.has(f.name + ':' + f.size)));
      });
      if (fileInputRef.current) fileInputRef.current.value = '';
    }
    function removeFile(i) { setFiles(prev => prev.filter((_, idx) => idx !== i)); }

    function goStep(n) { setStep(n); setMaxReached(m => Math.max(m, n)); }

    // Upload bytes to a signed URL, then enqueue the bg_remove batch.
    async function startRemoval() {
      if (!files.length) return;
      setBusy(true); setError(null);
      try {
        // 1) request signed upload URLs
        const up = await api('/images/uploads', { method: 'POST', body: { files: files.map(f => ({ name: f.name })) } });
        if (!up.ok) { setError(up.error || tx('page.bgremove.err_prepare_uploads', null, 'Could not prepare uploads.')); setBusy(false); return; }
        const minted = up.data.files || [];
        const bId = up.data.batch_id;
        // 2) PUT each file's bytes to its signed upload URL
        for (let i = 0; i < minted.length; i++) {
          const m = minted[i];
          const f = files.find(x => x.name === m.source_name) || files[i];
          if (!f) continue;
          // Supabase signed-upload PUT requires x-upsert (matches the proven convert
          // pipeline in FileManager.jsx against the same file-conversions bucket).
          // Without it the direct-to-Supabase PUT is rejected 400. Paths are fresh
          // UUIDs so upsert never overwrites a real prior object.
          const putResp = await fetch(m.upload_url, { method: 'PUT', body: f, headers: { 'Content-Type': f.type || 'application/octet-stream', 'x-upsert': 'true' } });
          if (!putResp.ok) { setError(tx('page.bgremove.err_upload_failed', { name: m.source_name, status: putResp.status }, 'Upload failed for {name} ({status}).')); setBusy(false); return; }
        }
        // 3) enqueue bg_remove
        const rb = await api('/images/remove-bg', { method: 'POST', body: {
          batch_id: bId,
          files: minted.map(m => ({ source_path: m.source_path, source_name: m.source_name })),
          options: { ratio },
        } });
        if (!rb.ok) {
          if (rb.status === 402) setError((rb.data && rb.data.error) || tx('page.bgremove.err_over_allowance', null, 'You are over your image allowance for this period.'));
          else setError(rb.error || tx('page.bgremove.err_start_removal', null, 'Could not start background removal.'));
          setBusy(false); return;
        }
        setBatchId(bId);
        setOpId(rb.data.operation_id);
        setResults(minted.map(m => ({ file_id: null, source_name: m.source_name, status: 'queued', previewUrl: null })));
        setBusy(false);
        goStep(2);
      } catch (e) {
        setError(tx('page.bgremove.err_unexpected', { msg: (e.message || e) }, 'Unexpected error: {msg}')); setBusy(false);
      }
    }

    // Poll the batch while on the process step.
    useEffect(() => {
      if (step !== 2 || !batchId) return;
      let stopped = false;
      async function tick() {
        const r = await api('/images/batch/' + encodeURIComponent(batchId));
        if (stopped) return;
        if (!r.ok) { setError(r.error || tx('page.bgremove.err_batch_status', null, 'Could not read batch status.')); return; }
        setStatus(r.data);
        setResults(r.data.files || []);
        if (r.data.complete) {
          if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }
          loadPreviews(r.data.files || []);
          loadAllowance();
        }
      }
      tick();
      pollRef.current = setInterval(tick, 2000);
      return () => { stopped = true; if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; } };
    }, [step, batchId]); // eslint-disable-line

    async function loadPreviews(rows) {
      const out = [];
      for (const row of rows) {
        let previewUrl = null;
        if (row.status === 'done') {
          const b = await apiBlob('/images/file/' + encodeURIComponent(row.file_id));
          if (b.ok) { previewUrl = URL.createObjectURL(b.blob); objectUrls.current.push(previewUrl); }
        }
        out.push({ ...row, previewUrl });
      }
      setResults(out);
    }

    async function download(row) {
      const b = await apiBlob('/images/file/' + encodeURIComponent(row.file_id) + '?download=1');
      if (!b.ok) { setError(b.error || tx('page.bgremove.err_download', null, 'Download failed.')); return; }
      const url = URL.createObjectURL(b.blob);
      const a = document.createElement('a');
      a.href = url; a.download = (row.source_name || 'image').replace(/\.[^.]+$/, '') + '-nobg.png';
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => { try { URL.revokeObjectURL(url); } catch (_) {} }, 1000);
    }

    function reset() {
      objectUrls.current.forEach(u => { try { URL.revokeObjectURL(u); } catch (_) {} });
      objectUrls.current = [];
      setStep(0); setMaxReached(0); setFiles([]); setRatio('original');
      setBatchId(null); setOpId(null); setStatus(null); setResults([]); setError(null);
    }

    /* ── step bodies ─────────────────────────────────────────────── */
    function AllowancePill() {
      if (!allowance) return null;
      const unlimited = allowance.allowance === -1;
      const txt = unlimited ? tx('page.bgremove.allowance_unlimited', null, 'Unlimited this period')
        : tx('page.bgremove.allowance_left', { remaining: allowance.remaining, total: allowance.allowance }, '{remaining} of {total} images left this period');
      return h('div', { className: 'wiz-eyebrow', style: { display: 'inline-flex', alignItems: 'center', gap: 6, marginBottom: 'var(--sp-4)' } },
        h(WIcon, { name: 'image', size: 13 }), txt);
    }

    function ErrorNotice() {
      if (!error) return null;
      return h('div', { style: { marginBottom: 'var(--sp-4)' } },
        h(NoticeCard, { accent: 'var(--red)', icon: 'alert-triangle', title: tx('page.bgremove.notice_heads_up', null, 'Heads up'), body: error }));
    }

    function StepUpload() {
      return h('div', null,
        h(AllowancePill),
        h(ErrorNotice),
        h('input', { ref: fileInputRef, type: 'file', accept: 'image/*', multiple: true, onChange: onPickFiles, style: { display: 'none' } }),
        h('button', {
          type: 'button', className: 'wiz-btn wiz-btn-tool wiz-focusable', onClick: () => fileInputRef.current && fileInputRef.current.click(),
        }, h(WIcon, { name: 'image', size: 16 }), files.length ? tx('page.bgremove.add_more_images', null, 'Add more images') : tx('page.bgremove.choose_images', null, 'Choose images')),
        files.length === 0
          ? h('p', { style: { marginTop: 'var(--sp-4)', color: 'var(--text-2)', fontSize: '.88rem' } }, tx('page.bgremove.upload_hint', null, 'PNG, JPG, WEBP, GIF, BMP or TIFF. Up to a batch at a time.'))
          : h('div', { style: { marginTop: 'var(--sp-4)', display: 'flex', flexDirection: 'column', gap: 'var(--sp-2)' } },
              files.map((f, i) => h('div', { key: f.name + i, className: 'wiz-card', style: { display: 'flex', alignItems: 'center', gap: 'var(--sp-3)', padding: '10px var(--sp-4)' } },
                h(WIcon, { name: 'image', size: 16, style: { color: 'var(--beam)' } }),
                h('span', { style: { flex: 1, minWidth: 0, fontSize: '.86rem', color: 'var(--text)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, f.name),
                h('span', { style: { fontFamily: 'var(--font-mono)', fontSize: '.72rem', color: 'var(--muted)' } }, (f.size / 1024 | 0) + ' KB'),
                h('button', { type: 'button', className: 'wiz-focusable', 'aria-label': tx('page.bgremove.remove_file_aria', { name: f.name }, 'Remove {name}'), onClick: () => removeFile(i), style: { background: 'none', border: 'none', cursor: 'pointer', color: 'var(--dim)', display: 'inline-flex', padding: 4 } }, h(WIcon, { name: 'x', size: 15 }))
              ))
            )
      );
    }

    function StepOptions() {
      return h('div', null,
        h(AllowancePill),
        h(ErrorNotice),
        h('div', { className: 'wiz-eyebrow', style: { marginBottom: 'var(--sp-3)' } }, tx('page.bgremove.output_crop', null, 'Output crop')),
        h(ModeSelectorGroup, { legend: tx('page.bgremove.output_crop', null, 'Output crop'), options: RATIO_OPTIONS, selected: ratio, onSelect: setRatio }),
        h('div', { style: { marginTop: 'var(--sp-5)' } },
          h(NoticeCard, { accent: 'var(--beam)', icon: 'info', title: tx('page.bgremove.notice_transparent_title', null, 'Transparent PNG'), body: (files.length === 1
            ? tx('page.bgremove.transparent_body_one', { n: files.length }, 'We cut out the subject and return a transparent PNG you can drop onto any client site. {n} image ready.')
            : tx('page.bgremove.transparent_body_many', { n: files.length }, 'We cut out the subject and return a transparent PNG you can drop onto any client site. {n} images ready.')) }))
      );
    }

    function StepProcess() {
      const done = results.filter(r => r.status === 'done').length;
      const failed = results.filter(r => r.status === 'failed').length;
      const complete = status && status.complete;
      return h('div', null,
        h(ErrorNotice),
        h('div', { className: 'wiz-eyebrow', style: { marginBottom: 'var(--sp-3)' } },
          complete
            ? (tx('page.bgremove.count_ready', { n: done }, '{n} ready') + (failed ? tx('page.bgremove.count_failed', { n: failed }, ' / {n} failed') : ''))
            : (results.length === 1
                ? tx('page.bgremove.processing_one', { n: results.length }, 'Processing {n} image...')
                : tx('page.bgremove.processing_many', { n: results.length }, 'Processing {n} images...'))),
        h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(160px, 1fr))', gap: 'var(--sp-4)' } },
          results.map((r, i) => h('div', { key: (r.file_id || r.source_name) + i, className: 'wiz-card', style: { padding: 'var(--sp-3)', display: 'flex', flexDirection: 'column', gap: 8 } },
            h('div', {
              style: {
                height: 120, borderRadius: 'var(--r)', display: 'flex', alignItems: 'center', justifyContent: 'center',
                background: 'repeating-conic-gradient(var(--surface-2) 0% 25%, var(--surface-3) 0% 50%) 50% / 16px 16px',
                overflow: 'hidden',
              },
            },
              r.previewUrl
                ? h('img', { src: r.previewUrl, alt: r.source_name, style: { maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' } })
                : h('span', { style: { color: r.status === 'failed' ? 'var(--red)' : 'var(--muted)', display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: '.74rem', fontFamily: 'var(--font-mono)' } },
                    h(WIcon, { name: r.status === 'failed' ? 'alert-triangle' : 'refresh', size: 14 }),
                    r.status === 'failed' ? tx('page.bgremove.status_failed', null, 'Failed') : (r.status === 'done' ? tx('page.bgremove.status_loading', null, 'Loading') : (r.status || 'queued')))
            ),
            h('span', { style: { fontSize: '.76rem', color: 'var(--text-2)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, r.source_name),
            r.status === 'done' && r.file_id && h('button', { type: 'button', className: 'wiz-btn wiz-btn-tool wiz-focusable', style: { fontSize: '.72rem', padding: '6px 10px' }, onClick: () => download(r) },
              h(WIcon, { name: 'download', size: 14 }), tx('page.bgremove.download_png', null, 'Download PNG'))
          ))
        ),
        complete && h('div', { style: { marginTop: 'var(--sp-5)' } },
          h('button', { type: 'button', className: 'wiz-btn wiz-btn-secondary wiz-focusable', onClick: reset },
            h(WIcon, { name: 'refresh', size: 15 }), tx('page.bgremove.remove_more', null, 'Remove more')))
      );
    }

    const body = step === 0 ? h(StepUpload) : step === 1 ? h(StepOptions) : h(StepProcess);

    const footer = {
      canBack: step > 0 && step < 2 && !busy,
      onBack: () => goStep(step - 1),
      canSkip: false,
      isLast: step === 2,
      onNext: () => { if (step === 0) { if (!files.length) { setError(tx('page.bgremove.add_one_first', null, 'Add at least one image first.')); return; } goStep(1); } else if (step === 1) { startRemoval(); } },
      onFinish: reset,
      nextLabel: step === 1 ? (busy ? tx('page.bgremove.starting', null, 'Starting...') : tx('page.bgremove.remove_background', null, 'Remove background')) : tx('page.bgremove.next', null, 'Next'),
      finishLabel: tx('common.done', null, 'Done'),
    };

    return h('div', { style: { maxWidth: 980, margin: '0 auto', padding: 'var(--sp-5)' } },
      h(WizardShell, {
        eyebrow: tx('page.bgremove.eyebrow', null, 'Image Tools'),
        title: tx('page.bgremove.title', null, 'Background Remover'),
        subtitle: tx('page.bgremove.subtitle', null, 'Upload images, remove the background, and download transparent PNGs ready to drop onto any client site.'),
        steps: STEPS, current: step, maxReached, stepVariant: 'numbered',
        onStepClick: (i) => { if (i <= maxReached && step !== 2) goStep(i); },
        footer,
      }, body)
    );
  }

  window.ImageBgRemoveWizard = ImageBgRemoveWizard;
  window.WPSB = window.WPSB || {};
  window.WPSB.ImageBgRemoveWizard = ImageBgRemoveWizard;
  console.log('[WPSB Wizard] Background Remover loaded (Increment 3). Railway:', RAILWAY_URL);
})();
