// ════════════════════════════════════════════════════════════════════════
// PropMystro · M3 · pm-d-inspect.jsx  (faithful port of pm-inspect*.jsx)
// Routine inspections: the master HMO template (9 sections, verbatim; HMO-only
// sections drop for single-lets), the HMO-3mo / other-6mo schedule, the runner
// (section rail, Yes/No/N/A + comments + photos, progress, inspector
// declaration e-sign), and the report with the landlord fail→ticket approval
// (checkbox picker, nothing auto-raised). Tables: inspections · tickets.
// Gated by 'inspect_mobile' (Professional). → window.PMInspectHub
// ════════════════════════════════════════════════════════════════════════
(function () {
  const { useState, useEffect, useCallback } = React;

  // ── master template (verbatim from pm-inspect-data.jsx) ─────────────────
  const TEMPLATE = [
    { ref: '1', title: 'Driveway & Entrance', items: [
      { ref: '1.1', q: 'Walls, drainage, and paving in sound condition?' },
      { ref: '1.2', q: 'Is the driveway clean and free of obstruction?' },
      { ref: '1.3', q: 'No refuse or rubbish stored (other than in bins)?' },
      { ref: '1.4', q: 'Exterior front door, frame, handle & lock in good condition and working order?', photoRequired: true },
      { ref: '1.5', q: 'Porch — area free from obstacles and in a clean condition?' },
      { ref: '1.5.1', q: 'Is there lighting in the porch area?' },
    ] },
    { ref: '2', title: 'Communal Areas & Stairs', hmoOnly: true, items: [
      { ref: '2.1', q: 'Are the communal hallways clean and free from obstruction and clutter?' },
      { ref: '2.2', q: 'Are the lights and light switches in the communal areas in good working order?' },
      { ref: '2.3', q: 'Walls and ceilings in communal areas in good condition and free from damp/condensation?' },
      { ref: '2.4', q: 'Visible electrical fittings, sockets, fixtures & consumer units in good condition (no exposed wiring / overheating)?' },
      { ref: '2.5', q: 'Is the flooring in good condition and free from trip hazards?' },
      { ref: '2.6', q: 'Are the stairs in good condition and free of clutter and hazards?' },
    ] },
    { ref: '3', title: 'Entrance, Living Room & Kitchen', items: [
      { ref: '3.1', q: 'Entrance door, frame, handle & lock in good condition and working order?' },
      { ref: '3.2', q: 'Visible electrical fittings, sockets, fixtures & consumer units in good condition (no exposed wiring / overheating)?' },
      { ref: '3.3', q: 'Are the lights and light switches in good working order?' },
      { ref: '3.4', q: 'Walls and ceiling in good condition and free from damp and condensation?' },
      { ref: '3.5', q: 'Are the radiators in good working order — confirmed with tenant?' },
      { ref: '3.6', q: 'Is the flooring in good condition and free from trip hazards?' },
      { ref: '3.7', q: 'Windows and window sills in good condition and open/close properly?' },
      { ref: '3.8', q: 'Kitchen units and worktops clean and in good condition, free of damage?' },
      { ref: '3.9', q: 'Is the hob in working order?' },
      { ref: '3.10', q: 'Hob extractor fan in working order and used regularly to avoid condensation?' },
      { ref: '3.11', q: 'Sink clean and tap in good working order, free from leaks?' },
      { ref: '3.12', q: 'Is there hot and cold water?' },
      { ref: '3.13', q: 'Confirm with tenant the washing machine (if any) is working with no leaks?' },
    ] },
    { ref: '4', title: 'Health & Safety — Alarms & Boiler', items: [
      { ref: '4.1', q: 'Smoke alarms in place and working? Note location, audible test, tenant confirmation.', photoRequired: true },
      { ref: '4.2', q: 'Carbon monoxide alarms in place and working? Note location, audible test, tenant confirmation.', photoRequired: true },
      { ref: '4.3', q: 'If boiler not in kitchen, is there an additional CO alarm? Note location, photo & test.' },
      { ref: '4.4', q: 'Is there a fire blanket in the property? Confirm location and photograph.', photoRequired: true },
      { ref: '4.5', q: 'Has the tenant confirmed that all alarms are working?' },
    ] },
    { ref: '5', title: 'Bathroom', items: [
      { ref: '5.1', q: 'Door, frame, handle & lock in good condition and working order?' },
      { ref: '5.2', q: 'Visible electrical fittings, sockets, fixtures & consumer units in good condition (no exposed wiring / overheating)?' },
      { ref: '5.3', q: 'Flooring in good condition and free from trip hazards? Note flooring type.' },
      { ref: '5.4', q: 'Lights and light switch in good working order?' },
      { ref: '5.5', q: 'Walls and ceiling in good condition and free from damp/condensation? Note tiled or painted.' },
      { ref: '5.6', q: 'Windows and window sills in good condition and open/close properly?' },
      { ref: '5.7', q: 'Radiators in good working order — confirmed with tenant?' },
      { ref: '5.8', q: 'Is there hot and cold water?' },
      { ref: '5.9', q: 'Extractor fan in working order and used regularly?' },
      { ref: '5.10', q: 'Toilet clean? Flush working? Tenant confirmed no leaks?' },
      { ref: '5.11', q: 'Basin clean and taps working properly with no leaks?' },
      { ref: '5.12', q: 'Shower unit/cubicle clean, taps/heads working with no leaks?' },
      { ref: '5.13', q: 'Shower door clean and in good working order with no damage/leaks?' },
      { ref: '5.14', q: 'Bath / bath panel clean, taps/heads working with no leaks?' },
      { ref: '5.15', q: 'Bathroom floor and other areas kept dry by tenant?' },
    ] },
    { ref: '6', title: 'Bedroom', items: [
      { ref: '6.1', q: 'Door, frame, handle & lock in good condition and working order?' },
      { ref: '6.2', q: 'Visible electrical fittings, sockets, fixtures & consumer units in good condition (no exposed wiring / overheating)?' },
      { ref: '6.3', q: 'Flooring in good condition and free from trip hazards?' },
      { ref: '6.4', q: 'Lights and light switch in good working order?' },
      { ref: '6.5', q: 'Walls and ceiling in good condition and free from damp/condensation?' },
      { ref: '6.6', q: 'Windows and window sills in good condition and open/close properly?' },
      { ref: '6.7', q: 'Radiators in good working order — confirmed with tenant?' },
    ] },
    { ref: '7', title: 'Noticeboard', hmoOnly: true, items: [
      { ref: '7.1', q: 'Is the council HMO licence displayed?', photoRequired: true },
      { ref: '7.2', q: 'Are the waste disposal rules displayed?' },
      { ref: '7.3', q: 'Are the condensation / damp posters displayed?' },
      { ref: '7.4', q: 'Contact info details and complaints handling provided?' },
      { ref: '7.5', q: 'Is there a poster for fire safety instructions?' },
      { ref: '7.6', q: 'Notice confirming Gas Safety Certificate, EICR & EPC emailed to tenant?' },
    ] },
    { ref: '8', title: 'Garden', items: [
      { ref: '8.1', q: 'Is the garden kept clean and without rubbish and clutter?' },
      { ref: '8.2', q: 'Are waste bins for the property present and clearly labelled?' },
      { ref: '8.3', q: 'Are the fences and perimeter secure and free of damage?' },
    ] },
    { ref: '9', title: 'Overall Tenant Compliance', items: [
      { ref: '9.1', q: 'Does the tenant keep the property in a clean and tidy manner?' },
      { ref: '9.2', q: 'Is the property free from any signs of leaks?' },
      { ref: '9.3', q: 'Is the property free of pests?' },
      { ref: '9.4', q: 'Occupants confirmed — count, all one household, no others? Note count in comments.' },
      { ref: '9.5', q: 'Are there any signs of subletting?', failOn: 'yes' },
      { ref: '9.6', q: 'Reminded tenant to ventilate regularly to prevent damp/condensation?' },
      { ref: '9.7', q: 'Reminded tenant to check fire and CO alarms regularly?' },
      { ref: '9.8', q: 'Are there any other matters to note?', failOn: 'never' },
    ] },
  ];
  const templateFor = (p) => TEMPLATE.filter(s => !s.hmoOnly || (p && p.type === 'hmo'));
  const itemCount = (p) => templateFor(p).reduce((n, s) => n + s.items.length, 0);
  const itemByRef = (ref) => { for (const s of TEMPLATE) { const it = s.items.find(i => i.ref === ref); if (it) return { section: s, item: it }; } return null; };
  const itemFailed = (item, a) => { if (!a) return false; const rule = item.failOn || 'no'; return rule === 'never' ? false : a === rule; };

  const freqMonths = (p) => p && p.type === 'hmo' ? 3 : 6;
  const typeLabel = (p) => p && p.type === 'hmo' ? 'HMO routine (quarterly)' : 'Routine (6-monthly)';
  const todayISO = () => new Date().toISOString().slice(0, 10);
  const fmt = (d) => d ? new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : '—';
  const addMonths = (i, n) => { const d = new Date(i + 'T00:00:00Z'); d.setUTCMonth(d.getUTCMonth() + n); return d.toISOString().slice(0, 10); };
  const daysUntil = (i) => Math.round((new Date(i + 'T00:00:00Z') - new Date(todayISO() + 'T00:00:00Z')) / 864e5);
  function Spin() { return <span className="spin dark" />; }

  function scheduleOf(insps, property) {
    const inProgress = insps.find(i => i.property_id === property.id && i.status === 'in_progress');
    const last = insps.filter(i => i.property_id === property.id && i.status === 'completed').sort((a, b) => (b.completed_at || '').localeCompare(a.completed_at || ''))[0];
    const due = last ? addMonths(last.completed_at, freqMonths(property)) : todayISO();
    const days = daysUntil(due);
    return { inProgress, last, due, days, status: inProgress ? 'running' : days < 0 ? 'overdue' : days <= 30 ? 'due' : 'ok' };
  }
  const failsOf = (insp) => {
    const out = [];
    for (const [ref, a] of Object.entries(insp.answers || {})) {
      const m = itemByRef(ref);
      if (m && itemFailed(m.item, a.a)) out.push({ ref, section: m.section, item: m.item, answer: a });
    }
    return out.sort((x, y) => x.ref.localeCompare(y.ref, undefined, { numeric: true }));
  };

  // ── runner ───────────────────────────────────────────────────────────────
  function Runner({ sb, account, insp, property, onClose, onDone, toast }) {
    const sections = templateFor(property);
    const [si, setSi] = useState(0);
    const [answers, setAnswers] = useState(insp.answers || {});
    const [declaring, setDeclaring] = useState(false);
    const [signName, setSignName] = useState('');
    const [busy, setBusy] = useState(false);
    const total = itemCount(property);
    const answered = Object.values(answers).filter(a => a.a).length;
    const sec = sections[si];
    const setAns = (ref, patch) => setAnswers(s => ({ ...s, [ref]: { ...(s[ref] || {}), ...patch } }));
    const secDone = (s) => s.items.every(it => (answers[it.ref] || {}).a);

    const save = async (extra, msg) => {
      setBusy(true);
      const { error } = await sb.from('inspections').update({ answers, ...(extra || {}) }).eq('id', insp.id);
      setBusy(false);
      if (error) { toast(error.message); return false; }
      if (msg) toast(msg);
      return true;
    };
    const complete = async () => {
      if (!signName.trim()) return;
      const ok = await save({
        status: 'completed', completed_at: todayISO(), inspector_name: signName.trim(),
        declaration: { name: signName.trim(), at: new Date().toISOString(), text: 'I confirm this report is an accurate record of the inspection carried out.' },
        ticket_review: 'pending',
      }, 'Inspection completed — awaiting landlord review');
      if (ok) onDone();
    };

    return <div className="modal-scrim" onClick={undefined}><div className="modal" style={{ maxWidth: 880 }} onClick={e => e.stopPropagation()}>
      <div className="modal-head"><div>
        <div className="pmd-mono" style={{ fontSize: 11, color: 'var(--brand-deep)' }}>INSPECTION RUNNER · {typeLabel(property).toUpperCase()}</div>
        <h2>{property.address}</h2>
        <p className="modal-sub">{answered} of {total} items answered · photos required where marked</p>
      </div><button className="x" onClick={async () => { await save(null, 'Progress saved'); onClose(); }}>✕</button></div>
      <div className="insp-progress"><span style={{ width: Math.round(answered / Math.max(total, 1) * 100) + '%' }}></span></div>

      {!declaring ? <div className="insp-body">
        <div className="insp-rail">
          {sections.map((s, i) => <button key={s.ref} className={'insp-rail-item' + (i === si ? ' on' : '') + (secDone(s) ? ' done' : '')} onClick={() => setSi(i)}>
            <span className="pmd-mono">{s.ref}</span> {s.title} {secDone(s) && '✓'}
          </button>)}
          <button className="btn btn-primary btn-sm" style={{ margin: '10px 8px 4px' }} disabled={answered < total} title={answered < total ? (total - answered) + ' items left' : ''} onClick={() => setDeclaring(true)}>Finish & sign →</button>
        </div>
        <div className="insp-items">
          <h3 style={{ fontSize: 16, margin: '2px 0 10px' }}>{sec.ref} · {sec.title}{sec.hmoOnly ? <span className="badge none" style={{ marginLeft: 8 }}>HMO</span> : null}</h3>
          {sec.items.map(it => {
            const a = answers[it.ref] || {};
            return <div className={'insp-item' + (itemFailed(it, a.a) ? ' failed' : '')} key={it.ref}>
              <div className="insp-q"><span className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)' }}>{it.ref}</span> {it.q}{it.photoRequired && <span className="pmd-mono" style={{ fontSize: 9.5, color: 'var(--brand-deep)' }}> · 📷 photo</span>}</div>
              <div className="insp-controls">
                {['yes', 'no', 'na'].map(v => <button key={v} className={'insp-ans ' + v + (a.a === v ? ' on' : '')} onClick={() => setAns(it.ref, { a: a.a === v ? null : v, at: new Date().toISOString() })}>{v === 'na' ? 'N/A' : v[0].toUpperCase() + v.slice(1)}</button>)}
                <input className="pm-input insp-comment" placeholder="Comment…" value={a.c || ''} onChange={e => setAns(it.ref, { c: e.target.value })} />
              </div>
              {itemFailed(it, a.a) && <div className="insp-fail-note">⚑ Will be flagged for a follow-up ticket in the report.</div>}
            </div>;
          })}
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 12 }}>
            <button className="btn btn-ghost btn-sm" disabled={si === 0} onClick={() => setSi(si - 1)}>← {si > 0 ? sections[si - 1].title : ''}</button>
            {si < sections.length - 1 ? <button className="btn btn-primary btn-sm" onClick={() => setSi(si + 1)}>{sections[si + 1].title} →</button>
              : <button className="btn btn-primary btn-sm" disabled={answered < total} onClick={() => setDeclaring(true)}>Finish & sign →</button>}
          </div>
        </div>
      </div>
      : <div style={{ padding: '0 24px' }}>
        <div className="alert alert-info"><span className="ic">✍</span><div><b>Inspector declaration.</b> {answered} of {total} items recorded · {failsOf({ answers }).length} flagged. "I confirm this report is an accurate record of the inspection carried out."</div></div>
        <div className="field" style={{ maxWidth: 320 }}><label>Inspector — type your full name</label><input value={signName} autoFocus onChange={e => setSignName(e.target.value)} /></div>
        {signName.trim() && <div className="ag-signature">{signName}</div>}
        <div className="modal-foot" style={{ justifyContent: 'space-between', padding: '18px 0 24px' }}>
          <button className="btn btn-ghost" onClick={() => setDeclaring(false)}>← Back to items</button>
          <button className="btn btn-primary" style={{ width: 'auto' }} disabled={!signName.trim() || busy} onClick={complete}>{busy ? <Spin /> : 'Sign & complete inspection ✓'}</button>
        </div>
      </div>}
    </div></div>;
  }

  // ── report (landlord review + fail→ticket picker) ───────────────────────
  function Report({ sb, account, insp, property, onClose, onDone, toast }) {
    const fails = failsOf(insp);
    const already = (insp.generated_tickets || []).map(g => g.ref);
    const [picked, setPicked] = useState(() => Object.fromEntries(fails.filter(f => !already.includes(f.ref)).map(f => [f.ref, true])));
    const [busy, setBusy] = useState(false);
    const sections = templateFor(property);
    const pickedRefs = fails.filter(f => picked[f.ref] && !already.includes(f.ref)).map(f => f.ref);

    const approve = async (raise) => {
      setBusy(true);
      try {
        const generated = [...(insp.generated_tickets || [])];
        if (raise) {
          for (const f of fails.filter(x => pickedRefs.includes(x.ref))) {
            const { data, error } = await sb.from('tickets').insert({
              property_id: property.id, title: 'Inspection ' + f.ref + ': ' + f.item.q.replace(/\?$/, ''),
              category: /damp|condensation/i.test(f.item.q) ? 'damp_mould' : /electr|wiring|consumer/i.test(f.item.q) ? 'electrical' : /alarm|fire|co /i.test(f.item.q) ? 'compliance' : 'general',
              priority: /alarm|fire|co |damp/i.test(f.item.q) ? 'high' : 'normal', status: 'inbox', source: 'inspection',
              raised_by: insp.inspector_name || 'Inspector', raised_at: todayISO(),
              description: (f.answer.c || 'Flagged during routine inspection') + ' (item ' + f.ref + ', inspected ' + fmt(insp.completed_at) + ')',
              quote_amount: 0, cost: 0, photos: [],
            }).select('id').single();
            if (!error && data) generated.push({ ref: f.ref, ticket_id: data.id });
          }
        }
        const allRaised = fails.every(f => generated.some(g => g.ref === f.ref));
        const { error } = await sb.from('inspections').update({
          ticket_review: fails.length === 0 || allRaised || !raise ? (raise || fails.length === 0 ? 'approved' : 'dismissed') : 'pending',
          generated_tickets: generated,
        }).eq('id', insp.id);
        if (error) throw error;
        toast(raise && pickedRefs.length ? pickedRefs.length + ' ticket(s) raised · report filed' : 'Report filed');
        onDone();
      } catch (e) { toast(e.message); } finally { setBusy(false); }
    };

    return <div className="modal-scrim" onClick={onClose}><div className="modal" style={{ maxWidth: 720 }} onClick={e => e.stopPropagation()}>
      <div className="modal-head"><div>
        <div className="pmd-mono" style={{ fontSize: 11, color: 'var(--brand-deep)' }}>INSPECTION REPORT · {fmt(insp.completed_at)}</div>
        <h2>{property.address}</h2>
        <p className="modal-sub">{typeLabel(property)} · inspector {insp.inspector_name || '—'}{insp.declaration ? ' · declaration signed' : ''}</p>
      </div><button className="x" onClick={onClose}>✕</button></div>
      <div style={{ padding: '0 24px', maxHeight: '54vh', overflowY: 'auto' }}>
        <div className="rr-autofill" style={{ padding: '0 0 10px' }}>
          <div><span className="pmd-mono">ITEMS</span><strong>{Object.keys(insp.answers || {}).length}</strong></div>
          <div><span className="pmd-mono">FLAGGED</span><strong style={fails.length ? { color: 'var(--red)' } : null}>{fails.length}</strong></div>
          <div><span className="pmd-mono">REVIEW</span><strong>{insp.ticket_review === 'approved' ? 'Filed ✓' : insp.ticket_review === 'dismissed' ? 'Dismissed' : 'Pending'}</strong></div>
        </div>
        {fails.length > 0 && <React.Fragment>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
            <strong style={{ fontSize: 14 }}>Flagged items — pick which become maintenance tickets</strong>
            <button className="linkbtn" onClick={() => setPicked(Object.fromEntries(fails.filter(f => !already.includes(f.ref)).map(f => [f.ref, true])))}>Select all</button>
          </div>
          {fails.map(f => <label key={f.ref} className={'gate-row' + (picked[f.ref] && !already.includes(f.ref) ? ' on' : '')} style={{ marginBottom: 8, opacity: already.includes(f.ref) ? .55 : 1 }}>
            <input type="checkbox" disabled={already.includes(f.ref)} checked={already.includes(f.ref) ? true : !!picked[f.ref]} onChange={e => setPicked({ ...picked, [f.ref]: e.target.checked })} />
            <div><strong>{f.ref} · {f.section.title}</strong><div className="s">{f.item.q}{f.answer.c ? ' — “' + f.answer.c + '”' : ''}{already.includes(f.ref) ? ' · ticket already raised' : ''}</div></div>
          </label>)}
        </React.Fragment>}
        {fails.length === 0 && <div className="alert alert-ok"><span className="ic">✓</span><div>No items flagged — a clean inspection.</div></div>}
        {/* full answers, by section */}
        {sections.map(s => <div key={s.ref} style={{ margin: '10px 0' }}>
          <div className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)', marginBottom: 4 }}>{s.ref} · {s.title.toUpperCase()}</div>
          {s.items.map(it => { const a = (insp.answers || {})[it.ref] || {}; return <div key={it.ref} className="insp-report-row">
            <span className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)', minWidth: 34 }}>{it.ref}</span>
            <span style={{ flex: 1 }}>{it.q}{a.c ? <span style={{ color: 'var(--ink-faint)' }}> — {a.c}</span> : null}</span>
            <span className={'badge ' + (itemFailed(it, a.a) ? 'bad' : a.a === 'na' ? 'none' : a.a ? 'ok' : 'none')}>{a.a ? (a.a === 'na' ? 'N/A' : a.a.toUpperCase()) : '—'}</span>
          </div>; })}
        </div>)}
      </div>
      <div className="modal-foot" style={{ justifyContent: 'space-between', gap: 8 }}>
        <button className="btn btn-ghost" onClick={onClose}>Close</button>
        {insp.ticket_review !== 'approved' && <div style={{ display: 'flex', gap: 8 }}>
          {fails.length > 0 && <button className="btn btn-ghost" disabled={busy} onClick={() => approve(false)}>File without tickets</button>}
          <button className="btn btn-primary" style={{ width: 'auto' }} disabled={busy} onClick={() => approve(true)}>{busy ? <Spin /> : fails.length && pickedRefs.length ? 'Raise ' + pickedRefs.length + ' ticket(s) & file ✓' : 'Approve & file ✓'}</button>
        </div>}
      </div>
    </div></div>;
  }

  // ── hub ──────────────────────────────────────────────────────────────────
  function InspectHub({ sb, account, toast, openBilling, onOpenProperty }) {
    const entitled = account.features && account.features.inspect_mobile === true;
    const [insps, setInsps] = useState(null);
    const [props, setProps] = useState([]);
    const [runner, setRunner] = useState(null);   // inspection row
    const [report, setReport] = useState(null);
    const [upload, setUpload] = useState(false);
    const [busy, setBusy] = useState('');

    const load = useCallback(async () => {
      if (!entitled) return;
      const [i, p] = await Promise.all([
        sb.from('inspections').select('*').order('created_at', { ascending: false }),
        sb.from('properties').select('id,address,type'),
      ]);
      setInsps(i.data || []); setProps(p.data || []);
    }, [sb, entitled]);
    useEffect(() => { load(); }, [load]);

    if (!entitled) return <React.Fragment>
      <div className="pagehead"><h1>Inspections</h1><p>Routine inspections on a statutory-friendly cadence.</p></div>
      <div className="card card-pad"><div className="locked">
        <div className="lock-ico">🔍</div><h3>Inspections is a Professional feature</h3>
        <p>The full inspection template (HMO-aware), the on-site runner with photos and the inspector declaration, and the fail→ticket approval flow.</p>
        <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} onClick={openBilling}>Upgrade to Professional</button>
      </div></div>
    </React.Fragment>;
    if (insps === null) return <div className="card card-pad"><Spin /></div>;

    const propById = Object.fromEntries(props.map(p => [p.id, p]));
    const schedule = props.map(p => ({ p, s: scheduleOf(insps, p) })).sort((a, b) => a.s.days - b.s.days);
    const pending = insps.filter(i => i.status === 'completed' && i.ticket_review === 'pending');
    const recent = insps.filter(i => i.status === 'completed').slice(0, 8);

    const start = async (p) => {
      setBusy(p.id);
      const { data, error } = await sb.from('inspections').insert({
        property_id: p.id, type: p.type === 'hmo' ? 'hmo' : 'routine', scheduled_for: todayISO(),
        started_at: todayISO(), status: 'in_progress', answers: {}, ticket_review: 'none', generated_tickets: [],
      }).select('*').single();
      setBusy('');
      if (error) return toast(/inspect|feature/.test(error.message) ? 'Inspections need the Professional plan.' : error.message);
      setRunner(data);
    };

    return <React.Fragment>
      <div className="pagehead" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 14, flexWrap: 'wrap' }}>
        <div><h1>Inspections</h1><p>HMOs quarterly, everything else 6-monthly. Run on site, sign the declaration, then approve flagged items into maintenance tickets.</p></div>
        <div className="summary" style={{ margin: 0 }}>
          <div className={'sumcard' + (schedule.some(x => x.s.status === 'overdue') ? ' bad' : '')}><div className="n">{schedule.filter(x => x.s.status === 'overdue').length}</div><div className="l">Overdue</div></div>
          <div className={'sumcard' + (pending.length ? ' warn' : '')}><div className="n">{pending.length}</div><div className="l">Await approval</div></div>
        </div>
      </div>

      <div className="pm-toolbar">
        <button className="btn btn-ghost btn-sm" onClick={() => setUpload(true)}>↑ Upload past report</button>
      </div>

      {pending.length > 0 && <div className="card" style={{ marginBottom: 16 }}>
        <div className="card-head"><div><h3>Awaiting your approval</h3><div className="sub">completed reports with flagged items to review</div></div></div>
        {pending.map(i => { const p = propById[i.property_id] || {}; const f = failsOf(i); return <div className="row clickable" key={i.id} onClick={() => setReport(i)}>
          <span className="avatar" style={{ background: f.length ? 'var(--red-soft)' : 'var(--ok-soft)' }}>🔍</span>
          <div className="main"><div className="t">{p.address}</div><div className="s">completed {fmt(i.completed_at)} · {i.inspector_name || 'inspector'} · {f.length} flagged</div></div>
          <button className="btn btn-primary btn-sm" onClick={e => { e.stopPropagation(); setReport(i); }}>Review →</button>
        </div>; })}
      </div>}

      <div className="card" style={{ marginBottom: 16 }}>
        <div className="card-head"><div><h3>Schedule</h3><div className="sub">next due per property</div></div></div>
        {schedule.length === 0 && <div className="empty"><div className="ico">🔍</div><h3>No properties yet</h3><p>Add properties and their inspection schedule appears here.</p></div>}
        {schedule.map(({ p, s }) => <div className="row" key={p.id}>
          <span className={'hdot ' + (s.status === 'overdue' ? 'crit' : s.status === 'due' ? 'warn' : 'ok')}></span>
          <div className="main">
            <div className="t">{onOpenProperty ? <button className="linkbtn" onClick={() => onOpenProperty(p)}>{p.address}</button> : p.address}</div>
            <div className="s">{typeLabel(p)} · {s.last ? 'last ' + fmt(s.last.completed_at) + ' · ' : 'never inspected · '}due {fmt(s.due)}{s.days >= 0 ? ' · in ' + s.days + 'd' : ' · ' + Math.abs(s.days) + 'd overdue'}</div>
          </div>
          {s.inProgress ? <button className="btn btn-primary btn-sm" onClick={() => setRunner(s.inProgress)}>Resume →</button>
            : <button className="btn btn-ghost btn-sm" disabled={busy === p.id} onClick={() => start(p)}>{busy === p.id ? <Spin /> : 'Start inspection'}</button>}
        </div>)}
      </div>

      {recent.length > 0 && <div className="card">
        <div className="card-head"><div><h3>Recent reports</h3><div className="sub">{recent.length} completed</div></div></div>
        {recent.map(i => { const p = propById[i.property_id] || {}; const f = failsOf(i); return <div className="row clickable" key={i.id} onClick={() => setReport(i)}>
          <span className="avatar" style={{ background: 'var(--surface-2)' }}>📋</span>
          <div className="main"><div className="t">{p.address}</div><div className="s">{fmt(i.completed_at)} · {i.inspector_name || '—'} · {f.length ? f.length + ' flagged' : 'clean'}</div></div>
          <span className={'badge ' + (i.ticket_review === 'approved' ? 'ok' : i.ticket_review === 'pending' ? 'warn' : 'none')}>{i.ticket_review === 'approved' ? 'Filed' : i.ticket_review === 'pending' ? 'Review' : 'Filed'}</span>
        </div>; })}
      </div>}

      {runner && <Runner sb={sb} account={account} insp={runner} property={propById[runner.property_id]} onClose={() => { setRunner(null); load(); }} onDone={() => { setRunner(null); load(); }} toast={toast} />}
      {report && <Report sb={sb} account={account} insp={report} property={propById[report.property_id]} onClose={() => setReport(null)} onDone={() => { setReport(null); load(); }} toast={toast} />}
      {upload && <UploadPastReport sb={sb} account={account} props={props} onClose={() => setUpload(false)} onTranscribe={(insp) => { setUpload(false); setRunner(insp); }} onDone={() => { setUpload(false); load(); }} toast={toast} />}
    </React.Fragment>;
  }

  // ── upload a past inspection report (file it, or transcribe into the runner)
  function UploadPastReport({ sb, account, props, onClose, onTranscribe, onDone, toast }) {
    const sorted = props.slice().sort((a, b) => (a.address || '').localeCompare(b.address || ''));
    const [propertyId, setPropertyId] = useState((sorted[0] || {}).id || '');
    const [when, setWhen] = useState('');
    const [file, setFile] = useState(null);
    const [busy, setBusy] = useState('');
    const fileIt = async () => {
      if (!file || !propertyId) return;
      setBusy('file');
      try {
        const path = account.id + '/inspections/' + Date.now() + '-' + file.name.replace(/[^\w.\-]+/g, '_');
        const up = await sb.storage.from('documents').upload(path, file, { contentType: file.type || undefined });
        if (up.error) throw new Error(up.error.message);
        const { error } = await sb.from('certificates').insert({
          property_id: propertyId, type: 'hmo_inspection', inspected_date: when || null,
          file_name: file.name, storage_path: path, source_text: 'Uploaded past inspection report',
        });
        if (error) throw new Error(error.message);
        toast('Report filed under the property'); onDone();
      } catch (e) { toast(e.message); } finally { setBusy(''); }
    };
    const transcribe = async () => {
      if (!propertyId) return;
      setBusy('runner');
      const p = props.find(x => x.id === propertyId);
      const { data, error } = await sb.from('inspections').insert({
        property_id: propertyId, type: p && p.type === 'hmo' ? 'hmo' : 'routine',
        scheduled_for: when || todayISO(), started_at: when || todayISO(), status: 'in_progress',
        answers: {}, ticket_review: 'none', generated_tickets: [],
      }).select('*').single();
      setBusy('');
      if (error) return toast(error.message);
      toast('Transcribe the report into the runner, then sign it off');
      onTranscribe(data);
    };
    return <div className="modal-scrim" onClick={onClose}><div className="modal" style={{ maxWidth: 540 }} onClick={e => e.stopPropagation()}>
      <div className="modal-head"><div><div className="pmd-mono" style={{ fontSize: 11, color: 'var(--brand-deep)' }}>UPLOAD PAST REPORT</div><h2>Bring in an old inspection</h2><p className="modal-sub">File the PDF against the property, or transcribe it into the template so it counts toward the schedule.</p></div><button className="x" onClick={onClose}>✕</button></div>
      <div className="modal-form">
        <div className="field"><label>Property</label><select value={propertyId} onChange={e => setPropertyId(e.target.value)}>{sorted.map(p => <option key={p.id} value={p.id}>{p.address}</option>)}</select></div>
        <div className="field"><label>Inspection date</label><input type="date" value={when} onChange={e => setWhen(e.target.value)} /></div>
        <div className="field full"><label>Report file (PDF / photo — for filing)</label><input type="file" accept=".pdf,image/*" onChange={e => setFile(e.target.files[0] || null)} /></div>
      </div>
      <div className="modal-foot" style={{ justifyContent: 'flex-end', gap: 8 }}>
        <button className="btn btn-ghost" disabled={!propertyId || busy === 'runner'} onClick={transcribe}>{busy === 'runner' ? <Spin /> : 'Transcribe into runner →'}</button>
        <button className="btn btn-primary" style={{ width: 'auto' }} disabled={!file || !propertyId || busy === 'file'} onClick={fileIt}>{busy === 'file' ? <Spin /> : 'File under property ✓'}</button>
      </div>
    </div></div>;
  }

  window.PMInspectHub = InspectHub;
})();
