// ════════════════════════════════════════════════════════════════════════
// PropMystro · Documents (faithful rebuild) · pm-d-documents.jsx
// Multi-purpose, like the Apps Script "Uploads": (1) AI INGEST — drop/paste a
// cert, Claude extracts the fields (extract-document fn; vision for scans),
// you review + pick the property, Save files a CERTIFICATE row AND stores the
// original in per-account Storage; (2) VAULT — every stored file, open via
// short-lived signed URL, or delete. Manual fallback if AI isn't deployed.
// → window.PMDocuments
// ════════════════════════════════════════════════════════════════════════
(function () {
  const { useState, useEffect, useCallback, useRef } = React;

  const DOC_TYPES = [
    { v: 'gas_safety', l: 'Gas Safety (CP12)', cycle: 12 },
    { v: 'eicr', l: 'Electrical (EICR)', cycle: 60 },
    { v: 'epc', l: 'EPC', cycle: 120 },
    { v: 'deposit_protection', l: 'Deposit protection' },
    { v: 'right_to_rent', l: 'Right to Rent' },
    { v: 'hmo_licence', l: 'HMO licence', cycle: 60 },
    { v: 'selective_licence', l: 'Selective licence', cycle: 60 },
    { v: 'insurance', l: 'Landlord insurance', cycle: 12 },
    { v: 'passport_id', l: 'Passport / ID' },
    { v: 'info_sheet', l: 'Info sheet' },
    { v: 'other', l: 'Other document' },
  ];
  const DOC_LABEL = Object.fromEntries(DOC_TYPES.map(d => [d.v, d.l]));
  const CYCLE = Object.fromEntries(DOC_TYPES.map(d => [d.v, d.cycle || null]));
  const CATS = [['compliance', 'Compliance'], ['invoice', 'Invoice'], ['id', 'ID / RTR'], ['inventory', 'Inventory'], ['letter', 'Letter / notice'], ['photo', 'Photo'], ['other', 'Other']];
  const CAT_LABEL = Object.fromEntries(CATS.map(c => [c[0], c[1]]));

  const fmt = (d) => d ? new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : '—';
  const ext = (n) => (n.split('.').pop() || '').toUpperCase().slice(0, 4);
  const norm = (s) => (s || '').toUpperCase().replace(/[^A-Z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim();

  const SAMPLES = {
    gas_safety: 'LANDLORD GAS SAFETY RECORD (CP12)\nProperty: 14 Eldon Road, Harrow, HA1 5HU\nEngineer: Mark Reynolds  Gas Safe Reg: 215643\nInspection date: 12 May 2026\nNext inspection due: 12 May 2027\nAppliances tested: 2  Result: PASS  Certificate no: CP12-88421',
    eicr: 'ELECTRICAL INSTALLATION CONDITION REPORT (EICR)\nAddress: 14 Eldon Road, Harrow HA1 5HU\nInspected: 03/03/2026\nNext inspection: 03/03/2031\nOverall assessment: SATISFACTORY\nReport ref: EICR-2026-5510  Engineer: T. Okafor (NICEIC 60112)',
    epc: 'ENERGY PERFORMANCE CERTIFICATE\n14 Eldon Road, Harrow, HA1 5HU\nDate of assessment: 1 February 2026\nValid until: 1 February 2036\nCurrent energy rating: C (72)  Certificate number: 0123-4567-8910',
  };

  function Spin() { return <span className="spin dark" />; }

  function fileToBase64(file) {
    return new Promise((resolve, reject) => {
      const r = new FileReader();
      r.onload = () => { const s = String(r.result || ''); const i = s.indexOf(','); resolve(i >= 0 ? s.slice(i + 1) : s); };
      r.onerror = () => reject(new Error('Could not read file')); r.readAsDataURL(file);
    });
  }
  // loose address → property match (postcode or first address word)
  function matchProperty(addr, postcode, props) {
    if (!props) return null;
    const pc = norm(postcode), a = norm(addr);
    if (pc) { const m = props.find(p => norm(p.postcode) && norm(p.postcode) === pc); if (m) return m; }
    if (a) { const first = a.split(' ')[0]; const m = props.find(p => norm(p.address).split(' ')[0] === first && first); if (m) return m; }
    return null;
  }

  function Documents({ sb, account, toast }) {
    const [mode, setMode] = useState('ingest');   // ingest | vault
    return <React.Fragment>
      <div className="pm-seg" style={{ marginBottom: 16 }}>
        <button className={'pm-seg-btn' + (mode === 'ingest' ? ' on' : '')} onClick={() => setMode('ingest')}>Upload &amp; file</button>
        <button className={'pm-seg-btn' + (mode === 'vault' ? ' on' : '')} onClick={() => setMode('vault')}>Document vault</button>
      </div>
      {mode === 'ingest' ? <Ingest sb={sb} account={account} toast={toast} /> : <Vault sb={sb} account={account} toast={toast} />}
    </React.Fragment>;
  }

  // ── AI ingest ──────────────────────────────────────────────────────────
  function Ingest({ sb, account, toast }) {
    const [props, setProps] = useState([]);
    const [file, setFile] = useState(null);     // {name,mimeType,base64,size}
    const [text, setText] = useState('');
    const [useVision, setUseVision] = useState(false);
    const [busy, setBusy] = useState(false);
    const [extracted, setExtracted] = useState(null);
    const [pickedProp, setPickedProp] = useState('');
    const [err, setErr] = useState('');
    const [done, setDone] = useState(null);
    const fileRef = useRef(null);

    useEffect(() => { sb.from('properties').select('id,address,postcode').then(r => setProps(r.data || [])); }, [sb]);

    const onFile = async (file0) => {
      if (!file0) return; setErr(''); setExtracted(null); setDone(null);
      if (file0.size > 8 * 1048576) return setErr('File too large (max 8 MB).');
      try {
        const base64 = await fileToBase64(file0);
        setFile({ name: file0.name, mimeType: file0.type, base64, size: file0.size });
        if (file0.type.startsWith('image/')) setUseVision(true);
        else if (file0.type === 'application/pdf') setUseVision(true);
        else if (file0.type.startsWith('text/')) { setText(await file0.text()); setUseVision(false); }
      } catch (e) { setErr('Could not read file: ' + e.message); }
    };

    const extract = async () => {
      setErr(''); setExtracted(null); setDone(null);
      const canVision = file && useVision && (file.mimeType === 'application/pdf' || (file.mimeType || '').startsWith('image/'));
      if (!canVision && !text.trim()) return setErr('Drop a file or paste some text first.');
      setBusy(true);
      try {
        const body = canVision ? { base64: file.base64, mimeType: file.mimeType } : { text };
        const { data, error } = await sb.functions.invoke('extract-document', { body });
        if (error || !data || data.error || !data.extracted) throw new Error((data && data.error) || (error && error.message) || 'no result');
        const ex = data.extracted;
        setExtracted(ex);
        const m = matchProperty(ex.propertyAddress, ex.postcode, props);
        setPickedProp(m ? m.id : '');
      } catch (e) {
        // graceful fallback → manual review with empty fields
        setExtracted({ documentType: 'gas_safety', _manual: true });
        setErr('AI extraction unavailable — fill the fields manually below. (Deploy the extract-document function to enable automatic reading.)');
      } finally { setBusy(false); }
    };

    const upd = (k, v) => setExtracted(prev => ({ ...prev, [k]: v }));

    const save = async () => {
      setErr('');
      if (!pickedProp) return setErr('Pick a property to file this against.');
      if (!extracted.documentType) return setErr('Choose a document type.');
      setBusy(true);
      // 1 · store the original (if a file was dropped)
      let storage_path = null, fileName = file ? file.name : null;
      if (file) {
        const safe = file.name.replace(/[^\w.\-]+/g, '_');
        storage_path = account.id + '/' + pickedProp + '/' + Date.now() + '-' + safe;
        const blob = await (await fetch('data:' + (file.mimeType || 'application/octet-stream') + ';base64,' + file.base64)).blob();
        const up = await sb.storage.from('documents').upload(storage_path, blob, { contentType: file.mimeType || undefined });
        if (up.error) { setBusy(false); return setErr('Upload failed: ' + up.error.message); }
      }
      // 2 · compute expiry from cycle if absent
      let expiry = extracted.expiryDate || null;
      if (!expiry && extracted.inspectedDate && CYCLE[extracted.documentType]) {
        const d = new Date(extracted.inspectedDate);
        if (!isNaN(d)) { d.setMonth(d.getMonth() + CYCLE[extracted.documentType]); expiry = d.toISOString().slice(0, 10); }
      }
      // 3 · file the certificate
      const { error: ce } = await sb.from('certificates').insert({
        property_id: pickedProp, type: extracted.documentType,
        cert_number: extracted.certificateNumber || null, inspected_date: extracted.inspectedDate || null,
        expiry_date: expiry, engineer_name: extracted.engineerName || null, engineer_ref: extracted.engineerRef || null,
        result: extracted.result || null, band: extracted.band || null, tenant_name: extracted.tenantName || null,
        storage_path, file_name: fileName, source_text: (text || '').slice(0, 8000),
      });
      if (ce) { setBusy(false); return setErr(ce.message); }
      // 4 · also record in the document vault if a file was stored
      if (file) await sb.from('documents').insert({ filename: fileName, storage_path, mime_type: file.mimeType || null, category: 'compliance', property_id: pickedProp, linked_cert_id: null });
      setBusy(false);
      const prop = props.find(p => p.id === pickedProp);
      setDone({ type: extracted.documentType, address: prop ? prop.address : '' });
      setFile(null); setText(''); setExtracted(null); setPickedProp(''); setUseVision(false);
      toast('Filed as ' + (DOC_LABEL[extracted.documentType] || 'certificate')); 
    };

    const reset = () => { setFile(null); setText(''); setExtracted(null); setPickedProp(''); setErr(''); setUseVision(false); setDone(null); };

    return <div className="card">
      <div className="card-head"><div><h3>Upload &amp; file a document</h3><div className="sub">Drop a cert — Claude reads it, you review, it files automatically against the property</div></div></div>
      <div className="ingest-grid">
        {/* input */}
        <div>
          <div className="dropzone" onDragOver={e => { e.preventDefault(); e.currentTarget.classList.add('on'); }} onDragLeave={e => e.currentTarget.classList.remove('on')} onDrop={e => { e.preventDefault(); e.currentTarget.classList.remove('on'); onFile(e.dataTransfer.files[0]); }} onClick={() => fileRef.current && fileRef.current.click()}>
            <div className="dz-icon">↑</div>
            <div className="dz-h">Drop a PDF, image or photo of a certificate</div>
            <div className="dz-s">Typed or handwritten · CP12 · EICR · EPC · deposit · insurance · ID</div>
            <input ref={fileRef} type="file" accept=".pdf,.txt,image/*" style={{ display: 'none' }} onChange={e => onFile(e.target.files[0])} />
          </div>
          {file && <div className="file-chip"><span style={{ fontSize: 16 }}>📎</span><div style={{ flex: 1, minWidth: 0 }}><div style={{ fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{file.name}</div><div className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)' }}>{file.mimeType || 'unknown'} · {(file.size / 1024).toFixed(0)} KB</div></div>
            <label className="chk" title="Reads scans & handwriting"><input type="checkbox" checked={useVision} onChange={e => setUseVision(e.target.checked)} disabled={(file.mimeType || '').startsWith('text/')} /> Vision</label>
            <button className="btn btn-ghost btn-sm" onClick={() => { setFile(null); setUseVision(false); }}>✕</button></div>}
          <div className="sample-row"><span className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)' }}>Try a sample:</span>
            {['gas_safety', 'eicr', 'epc'].map(k => <button key={k} className="btn btn-ghost btn-sm" onClick={() => { setText(SAMPLES[k]); setFile(null); setUseVision(false); setExtracted(null); setDone(null); }}>{DOC_LABEL[k].split(' ')[0]}</button>)}</div>
          <label className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)', display: 'block', margin: '12px 0 6px' }}>OR PASTE THE TEXT</label>
          <textarea className="pm-textarea" value={text} onChange={e => setText(e.target.value)} rows={9} placeholder="Paste the document text here…" />
          <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
            <button className="btn btn-primary btn-sm" onClick={extract} disabled={busy || (!text.trim() && !(file && useVision))}>{busy ? <Spin /> : (file && useVision ? '👁 Extract with vision' : '✨ Extract')}</button>
            <button className="btn btn-ghost btn-sm" onClick={reset}>Clear</button>
          </div>
          {err && <div className="alert alert-info" style={{ marginTop: 10 }}><span className="ic">ℹ</span><div>{err}</div></div>}
        </div>

        {/* review */}
        <div className="review-pane">
          {done ? <div className="empty" style={{ padding: '30px 16px' }}><div className="ico" style={{ background: 'var(--ok-soft)' }}>✓</div><h3>Filed</h3><p>{DOC_LABEL[done.type] || 'Certificate'} added to {done.address || 'the property'} and stored in the vault.</p><button className="btn btn-ghost btn-sm" onClick={() => setDone(null)}>File another</button></div>
          : !extracted ? <div className="review-empty"><div className="rev-ico">▤</div><strong>Review &amp; file</strong><p>Drop or paste a document on the left — the extracted fields appear here for checking before anything is filed.</p></div>
          : <React.Fragment>
            <div style={{ fontWeight: 700, marginBottom: 10 }}>Review &amp; file{extracted.confidence != null ? <span className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)', marginLeft: 8 }}>conf {Math.round(extracted.confidence * 100)}%</span> : ''}</div>
            <div className="rev-field"><label>Property *</label><select value={pickedProp} onChange={e => setPickedProp(e.target.value)}><option value="">— pick a property —</option>{props.map(p => <option key={p.id} value={p.id}>{p.address}</option>)}</select></div>
            <div className="rev-field"><label>Document type *</label><select value={extracted.documentType || ''} onChange={e => upd('documentType', e.target.value)}>{DOC_TYPES.map(d => <option key={d.v} value={d.v}>{d.l}</option>)}</select></div>
            <div className="rev-row">
              <div className="rev-field"><label>Inspected</label><input type="date" value={extracted.inspectedDate || ''} onChange={e => upd('inspectedDate', e.target.value)} /></div>
              <div className="rev-field"><label>Expires</label><input type="date" value={extracted.expiryDate || ''} onChange={e => upd('expiryDate', e.target.value)} /></div>
            </div>
            <div className="rev-row">
              <div className="rev-field"><label>Certificate no.</label><input value={extracted.certificateNumber || ''} onChange={e => upd('certificateNumber', e.target.value)} /></div>
              <div className="rev-field"><label>Result / band</label><input value={extracted.result || extracted.band || ''} onChange={e => upd('result', e.target.value)} /></div>
            </div>
            <div className="rev-field"><label>Engineer / assessor</label><input value={extracted.engineerName || ''} onChange={e => upd('engineerName', e.target.value)} placeholder="optional" /></div>
            {!extracted.expiryDate && extracted.inspectedDate && CYCLE[extracted.documentType] && <div className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)', marginBottom: 8 }}>Expiry will auto-compute: inspected + {CYCLE[extracted.documentType]} months</div>}
            <button className="btn btn-primary btn-sm" onClick={save} disabled={busy}>{busy ? <Spin /> : 'File this document'}</button>
          </React.Fragment>}
        </div>
      </div>
    </div>;
  }

  // ── Vault ────────────────────────────────────────────────────────────
  function Vault({ sb, account, toast }) {
    const [docs, setDocs] = useState(null);
    const [cat, setCat] = useState('compliance');
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState('');
    const fileRef = useRef(null);

    const load = useCallback(async () => { const { data } = await sb.from('documents').select('*').order('created_at', { ascending: false }); setDocs(data || []); }, [sb]);
    useEffect(() => { load(); }, [load]);

    const onFile = async (e) => {
      const file = e.target.files && e.target.files[0]; e.target.value = ''; if (!file) return;
      setErr(''); if (file.size > 25 * 1048576) return setErr('Keep files under 25 MB.');
      setBusy(true);
      const safe = file.name.replace(/[^\w.\-]+/g, '_');
      const path = account.id + '/' + cat + '/' + Date.now() + '-' + safe;
      const up = await sb.storage.from('documents').upload(path, file, { contentType: file.type || undefined });
      if (up.error) { setBusy(false); return setErr('Upload failed: ' + up.error.message); }
      const { error } = await sb.from('documents').insert({ filename: file.name, storage_path: path, mime_type: file.type || null, category: cat });
      setBusy(false); if (error) return setErr(error.message);
      toast('Document uploaded'); load();
    };
    const open = async (d) => { const { data, error } = await sb.storage.from('documents').createSignedUrl(d.storage_path, 60); if (error) return toast('Could not open file'); window.open(data.signedUrl, '_blank'); };
    const remove = async (d) => { await sb.storage.from('documents').remove([d.storage_path]); await sb.from('documents').delete().eq('id', d.id); toast('Removed'); load(); };

    return <div className="card">
      <div className="card-head"><div><h3>Document vault</h3><div className="sub">{docs === null ? '…' : docs.length + ' file' + (docs.length === 1 ? '' : 's')} · stored privately per account</div></div>
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          <select value={cat} onChange={e => setCat(e.target.value)} className="pm-input" style={{ padding: '8px 11px', fontSize: 13 }}>{CATS.map(c => <option key={c[0]} value={c[0]}>{c[1]}</option>)}</select>
          <button className="btn btn-primary btn-sm" onClick={() => fileRef.current && fileRef.current.click()} disabled={busy}>{busy ? <Spin /> : '↑ Upload'}</button>
          <input ref={fileRef} type="file" style={{ display: 'none' }} onChange={onFile} />
        </div>
      </div>
      {err && <div className="card-pad" style={{ paddingBottom: 0 }}><div className="alert alert-err"><span className="ic">⚠</span><div>{err}</div></div></div>}
      {docs === null ? <div className="card-pad"><Spin /></div>
        : docs.length === 0 ? <div className="empty"><div className="ico">📁</div><h3>No documents yet</h3><p>Upload certificates, invoices, agreements or photos — or use “Upload &amp; file” to auto-extract and file a cert.</p></div>
        : docs.map(d => <div className="row" key={d.id}>
          <span className="avatar" style={{ background: 'var(--surface-2)', color: 'var(--ink-faint)', fontSize: 10, fontWeight: 700 }}>{ext(d.filename)}</span>
          <div className="main"><div className="t">{d.filename}</div><div className="s">{CAT_LABEL[d.category] || d.category} · {fmt(d.created_at)}</div></div>
          <button className="btn btn-ghost btn-sm" onClick={() => open(d)}>Open</button>
          <button className="btn btn-ghost btn-sm" onClick={() => remove(d)} style={{ padding: '8px 10px' }}>✕</button>
        </div>)}
    </div>;
  }

  window.PMDocuments = Documents;
})();
