// screens_scan.jsx — REAL receipt scanning. -> window.
// On-device OCR with Tesseract.js (loaded lazily from CDN), a parser that pulls
// line items / tax / merchant out of the recognized text, and an editable
// review step (OCR is never perfect, so the user confirms before assigning).

// ── Tesseract loader (lazy, once) ─────────────────────────────
let _tessPromise = null;
function loadTesseract() {
  if (window.Tesseract) return Promise.resolve(window.Tesseract);
  if (_tessPromise) return _tessPromise;
  _tessPromise = new Promise((resolve, reject) => {
    const sc = document.createElement('script');
    sc.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@5.1.1/dist/tesseract.min.js';
    sc.onload = () => window.Tesseract ? resolve(window.Tesseract) : reject(new Error('Scanner failed to load'));
    sc.onerror = () => { _tessPromise = null; reject(new Error('Could not reach the scanner — check your connection')); };
    document.head.appendChild(sc);
  });
  return _tessPromise;
}

// ── Image prep: downscale + grayscale + contrast for faster, cleaner OCR ──
function prepImage(file, maxW) {
  return new Promise((resolve, reject) => {
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      const scale = Math.min(1, maxW / img.width);
      const w = Math.max(1, Math.round(img.width * scale));
      const h = Math.max(1, Math.round(img.height * scale));
      const c = document.createElement('canvas'); c.width = w; c.height = h;
      const ctx = c.getContext('2d');
      ctx.drawImage(img, 0, 0, w, h);
      const preview = c.toDataURL('image/jpeg', 0.72); // colour, for the on-screen thumb
      // grayscale + mild contrast stretch → better recognition
      try {
        const id = ctx.getImageData(0, 0, w, h); const d = id.data;
        for (let i = 0; i < d.length; i += 4) {
          let g = 0.299 * d[i] + 0.587 * d[i + 1] + 0.114 * d[i + 2];
          g = (g - 128) * 1.28 + 128;
          g = g < 0 ? 0 : g > 255 ? 255 : g;
          d[i] = d[i + 1] = d[i + 2] = g;
        }
        ctx.putImageData(id, 0, 0);
      } catch (e) { /* tainted canvas etc — OCR the colour version */ }
      URL.revokeObjectURL(url);
      resolve({ ocr: c.toDataURL('image/png'), preview });
    };
    img.onerror = () => { URL.revokeObjectURL(url); reject(new Error("That image couldn't be opened")); };
    img.src = url;
  });
}

// ── Parser: recognized text → { items, tax, total, merchant } ─
function titleCaseWord(s) {
  return s.replace(/\w[\w']*/g, (w) => w.length <= 2 && w === w.toUpperCase() ? w : w[0].toUpperCase() + w.slice(1).toLowerCase());
}
function parseReceiptText(text) {
  const lines = String(text).split(/\r?\n/).map((l) => l.replace(/\s+/g, ' ').trim()).filter(Boolean);
  const priceRe = /(\d{1,4}[.,]\d{2})(?!\d)/g;
  const skip = /(sub\s*-?\s*total|amount due|balance|tax|gst|hst|pst|vat|tip|gratuity|service\s+(charge|fee)|change|cash|credit|debit|visa|master|amex|discover|\bcard\b|tender|account|approval|auth|\bref\b|server|cashier|table|guest|order|check\s*#|invoice|receipt|thank|visit|www|http|\btel\b|phone|^date|^time|\bqty\b|^#|points|loyalty|rounding|^total|grand total)/i;

  // merchant: first early line that's mostly letters and priceless
  let merchant = '';
  for (const l of lines.slice(0, 5)) {
    const letters = l.replace(/[^a-z]/gi, '').length;
    if (letters >= 3 && !priceRe.test(l)) { merchant = titleCaseWord(l.replace(/[^\w &'.\-]/g, '').trim()); break; }
    priceRe.lastIndex = 0;
  }

  const items = []; let tax = null, total = null;
  for (const line of lines) {
    priceRe.lastIndex = 0;
    const matches = [...line.matchAll(priceRe)];
    if (!matches.length) continue;
    const last = matches[matches.length - 1];
    const cents = Math.round(parseFloat(last[1].replace(',', '.')) * 100);
    const lower = line.toLowerCase();
    if (/\b(tax|gst|hst|pst|vat)\b/.test(lower) && !/sub\s*-?\s*total/.test(lower)) { if (tax == null) tax = cents; continue; }
    if (/\b(total|amount due|balance)\b/.test(lower)) { if (cents > (total || 0)) total = cents; continue; }
    if (skip.test(line)) continue;
    let name = line.slice(0, last.index).replace(/[$€£]/g, '').trim();
    name = name.replace(/^\d+\s*[xX@]?\s+/, '');     // strip leading "2 " / "2x " qty
    name = name.replace(/[.\-_\s]+$/, '').trim();
    if (name.replace(/[^a-z]/gi, '').length < 2) continue;  // needs real letters
    if (cents <= 0 || cents > 5000000) continue;
    items.push({ name: titleCaseWord(name), price: cents });
  }
  return { items, tax, total, merchant };
}

function dollarsToCents(str) {
  const n = parseFloat(String(str).replace(/[^0-9.,]/g, '').replace(',', '.'));
  return isFinite(n) ? Math.round(n * 100) : 0;
}

// ── Scan screen ───────────────────────────────────────────────
function ScanScreen({ state, set, nav, currency }) {
  const [phase, setPhase] = React.useState('aim');   // aim | working | review | error
  const [progress, setProgress] = React.useState(0);
  const [status, setStatus] = React.useState('');
  const [preview, setPreview] = React.useState('');
  const [err, setErr] = React.useState('');
  const [foundCount, setFoundCount] = React.useState(0);
  const [items, setItems] = React.useState([]);      // {id, name, amount(string)}
  const [merchant, setMerchant] = React.useState('');
  const [taxStr, setTaxStr] = React.useState('');
  const fileRef = React.useRef(null);
  const camRef = React.useRef(null);

  const blankRow = () => ({ id: 'oi' + Math.random().toString(36).slice(2, 8), name: '', amount: '' });

  const runOcr = async (file) => {
    setErr(''); setPreview(''); setProgress(0);
    setPhase('working'); setStatus('Preparing photo…');
    try {
      const { ocr, preview: pv } = await prepImage(file, 1500);
      setPreview(pv);
      setStatus('Loading scanner…');
      const Tesseract = await loadTesseract();
      setStatus('Reading the receipt…');
      const res = await Tesseract.recognize(ocr, 'eng', {
        logger: (m) => { if (m.status === 'recognizing text') setProgress(m.progress || 0); },
      });
      const parsed = parseReceiptText((res && res.data && res.data.text) || '');
      const list = parsed.items.map((it) => ({ id: blankRow().id, name: it.name, amount: (it.price / 100).toFixed(2) }));
      setFoundCount(list.length);
      setItems(list.length ? list : [blankRow()]);
      setMerchant(parsed.merchant || '');
      setTaxStr(parsed.tax ? (parsed.tax / 100).toFixed(2) : '');
      setPhase('review');
    } catch (e) {
      setErr(e && e.message ? e.message : 'Scan failed'); setPhase('error');
    }
  };

  const onPick = (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = '';
    if (file) runOcr(file);
  };
  const openPicker = () => fileRef.current && fileRef.current.click();
  const openCamera = () => camRef.current && camRef.current.click();

  // review editing
  const editRow = (id, patch) => setItems((l) => l.map((it) => it.id === id ? { ...it, ...patch } : it));
  const removeRow = (id) => setItems((l) => (l.length > 1 ? l.filter((it) => it.id !== id) : l));
  const addRow = () => setItems((l) => [...l, blankRow()]);

  const subtotalCents = items.reduce((a, it) => a + dollarsToCents(it.amount), 0);
  const taxCents = dollarsToCents(taxStr);
  const canConfirm = items.some((it) => dollarsToCents(it.amount) > 0);

  const confirm = () => {
    const clean = items
      .map((it, i) => ({ id: 'i' + Date.now() + '_' + i, name: (it.name || '').trim() || ('Item ' + (i + 1)), price: dollarsToCents(it.amount), assignees: [] }))
      .filter((it) => it.price > 0);
    if (!clean.length) return;
    const sub = clean.reduce((a, it) => a + it.price, 0);
    const rate = taxCents > 0 && sub > 0 ? taxCents / sub : 0;
    set({ entry: 'receipt', items: clean, taxRate: rate, includeTax: rate > 0, splitMode: 'items', merchant: merchant.trim(), subtotalCents: sub });
    nav('assign');
  };

  const fieldInput = {
    height: 46, width: '100%', borderRadius: 12, border: '1.5px solid var(--outline)', background: 'var(--surface-variant)',
    color: 'var(--on-surface)', fontFamily: 'var(--font-body)', fontWeight: 700, fontSize: 15, padding: '0 13px', outline: 'none',
  };

  const hiddenInput = (
    <React.Fragment>
      <input ref={fileRef} type="file" accept="image/*" onChange={onPick} style={{ display: 'none' }} />
      <input ref={camRef} type="file" accept="image/*" capture="environment" onChange={onPick} style={{ display: 'none' }} />
    </React.Fragment>
  );

  // ── AIM ──
  if (phase === 'aim') {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
        {hiddenInput}
        <ScreenHeader title="Scan receipt" onBack={() => nav('home')} />
        <div style={{ flex: 1, margin: '0 18px 14px', borderRadius: 28, position: 'relative', overflow: 'hidden',
          background: 'linear-gradient(160deg,#1a1f26,#0c0f14)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 16, padding: 24 }}>
          {['tl','tr','bl','br'].map((k) => (
            <div key={k} style={{ position: 'absolute',
              top: k[0]==='t'?20:'auto', bottom: k[0]==='b'?20:'auto', left: k[1]==='l'?20:'auto', right: k[1]==='r'?20:'auto',
              width: 30, height: 30, borderColor: 'var(--primary)', borderStyle: 'solid',
              borderWidth: `${k[0]==='t'?3:0}px ${k[1]==='r'?3:0}px ${k[0]==='b'?3:0}px ${k[1]==='l'?3:0}px`,
              borderTopLeftRadius: k==='tl'?10:0, borderTopRightRadius: k==='tr'?10:0, borderBottomLeftRadius: k==='bl'?10:0, borderBottomRightRadius: k==='br'?10:0 }} />
          ))}
          <div style={{ width: 78, height: 78, borderRadius: 99, background: 'rgba(255,255,255,0.08)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff' }}>
            <IconScan s={38} />
          </div>
          <div style={{ textAlign: 'center', color: '#fff', maxWidth: 260 }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 19, marginBottom: 6 }}>Snap your receipt</div>
            <div style={{ fontSize: 13.5, opacity: 0.72, lineHeight: 1.5 }}>Lay it flat in good light and get the whole list in frame. We’ll read the items, then you confirm.</div>
          </div>
        </div>
        <div style={{ padding: '0 18px 16px', display: 'flex', flexDirection: 'column', gap: 10, flexShrink: 0 }}>
          <Button onClick={openCamera} leading={<IconCamera s={21} />}>Take a photo</Button>
          <Button variant="ghost" onClick={openPicker} leading={<IconReceipt s={20} />}>Upload from library</Button>
        </div>
      </div>
    );
  }

  // ── WORKING ──
  if (phase === 'working') {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
        <ScreenHeader title="Scanning…" />
        <div style={{ flex: 1, margin: '0 18px 14px', borderRadius: 28, position: 'relative', overflow: 'hidden',
          background: '#0c0f14', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          {preview
            ? <img src={preview} alt="receipt" style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', opacity: 0.85 }} />
            : <div style={{ color: 'var(--on-surface-var)' }}>…</div>}
          <div style={{ position: 'absolute', left: '6%', right: '6%', height: 3, borderRadius: 9,
            background: 'var(--primary)', boxShadow: '0 0 16px 3px var(--primary)', animation: 'taScan 1.8s ease-in-out infinite' }} />
        </div>
        <div style={{ padding: '0 18px 20px' }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
            <span style={{ fontWeight: 700, color: 'var(--on-surface-var)', fontSize: 14 }}>{status}</span>
            {status.startsWith('Reading') && <span className="ta-num" style={{ fontWeight: 800, fontSize: 14 }}>{Math.round(progress * 100)}%</span>}
          </div>
          <div style={{ height: 8, borderRadius: 99, background: 'var(--surface-variant)', overflow: 'hidden' }}>
            <div style={{ height: '100%', width: `${status.startsWith('Reading') ? progress * 100 : 12}%`, background: 'var(--primary)', borderRadius: 99, transition: 'width .3s' }} />
          </div>
        </div>
      </div>
    );
  }

  // ── ERROR ──
  if (phase === 'error') {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
        {hiddenInput}
        <ScreenHeader title="Scan receipt" onBack={() => nav('home')} />
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center', padding: '0 32px', gap: 14 }}>
          <div style={{ width: 64, height: 64, borderRadius: 99, background: 'var(--surface-variant)', color: 'var(--on-surface-var)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><IconReceipt s={32} /></div>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 20 }}>Couldn’t scan that</div>
          <div style={{ fontSize: 14, color: 'var(--on-surface-var)', lineHeight: 1.5 }}>{err}</div>
        </div>
        <div style={{ padding: '0 18px 16px', display: 'flex', flexDirection: 'column', gap: 10, flexShrink: 0 }}>
          <Button onClick={openCamera} leading={<IconCamera s={20} />}>Take a photo</Button>
          <Button variant="ghost" onClick={openPicker} leading={<IconReceipt s={19} />}>Upload from library</Button>
          <Button variant="text" onClick={() => { setItems([blankRow()]); setMerchant(''); setTaxStr(''); setFoundCount(0); setPhase('review'); }}>Enter items manually</Button>
        </div>
      </div>
    );
  }

  // ── REVIEW ──
  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      {hiddenInput}
      <ScreenHeader title="Check the items" onBack={() => nav('home')}
        sub={foundCount ? `Found ${foundCount} item${foundCount === 1 ? '' : 's'} — fix anything off` : 'Add each item and its price'} />

      <div className="ta-scroll" style={{ flex: 1, minHeight: 0, padding: '2px 18px 8px' }}>
        <Label style={{ margin: '4px 2px 8px' }}>Place</Label>
        <input value={merchant} onChange={(e) => setMerchant(e.target.value)} placeholder="Merchant (optional)" style={{ ...fieldInput, marginBottom: 16 }} />

        <Label style={{ margin: '0 2px 8px' }}>Items</Label>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {items.map((it) => (
            <div key={it.id} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <input value={it.name} onChange={(e) => editRow(it.id, { name: e.target.value })} placeholder="Item name" style={{ ...fieldInput, flex: 1, minWidth: 0 }} />
              <input value={it.amount} onChange={(e) => editRow(it.id, { amount: e.target.value })} inputMode="decimal" placeholder="0.00"
                className="ta-num" style={{ ...fieldInput, width: 86, textAlign: 'right', flexShrink: 0 }} />
              <button className="ta-press" onClick={() => removeRow(it.id)} title="Remove" style={{ width: 38, height: 38, flexShrink: 0, borderRadius: 99, border: 'none', cursor: 'pointer',
                background: 'transparent', color: 'var(--on-surface-var)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <IconTrash s={19} />
              </button>
            </div>
          ))}
        </div>

        <button className="ta-press" onClick={addRow} style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, padding: '11px 0', marginTop: 10,
          border: '1.5px dashed var(--outline)', borderRadius: 12, background: 'transparent', cursor: 'pointer', color: 'var(--primary)', fontFamily: 'var(--font-body)', fontWeight: 700, fontSize: 14 }}>
          <IconPlus s={17} /> Add item
        </button>

        <Label style={{ margin: '20px 2px 8px' }}>Tax</Label>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <span style={{ flex: 1, fontWeight: 700, color: 'var(--on-surface-var)', fontSize: 14, paddingLeft: 2 }}>Tax from the receipt (optional)</span>
          <input value={taxStr} onChange={(e) => setTaxStr(e.target.value)} inputMode="decimal" placeholder="0.00" className="ta-num" style={{ ...fieldInput, width: 86, textAlign: 'right', flexShrink: 0 }} />
        </div>

        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginTop: 18, padding: '0 2px' }}>
          <span style={{ fontWeight: 700, color: 'var(--on-surface-var)' }}>Subtotal</span>
          <span className="ta-num" style={{ fontFamily: 'var(--font-display)', fontWeight: 'var(--num-weight)', fontSize: 22 }}>{fmt(subtotalCents, currency)}</span>
        </div>
      </div>

      <div style={{ background: 'var(--surface)', borderTopLeftRadius: 28, borderTopRightRadius: 28, padding: '12px 18px 14px', boxShadow: '0 -6px 24px rgba(0,0,0,0.05)', display: 'flex', gap: 10, flexShrink: 0 }}>
        <Button variant="ghost" full={false} onClick={openCamera} title="Rescan" style={{ flex: '0 0 auto', width: 56, padding: 0 }}><IconCamera s={20} /></Button>
        <Button disabled={!canConfirm} onClick={confirm} leading={<IconArrowR s={20} />}>Use these items</Button>
      </div>
    </div>
  );
}

Object.assign(window, { ScanScreen, parseReceiptText });
