// ════════════════════════════════════════════════════════════════════════
// PropMystro · M0 · pm-d-property.jsx  (faithful rebuild)
// Properties — the full module ported from the Apps Script app:
//   • PMPropertiesList  — searchable, owner-filterable table with owner
//     avatars, compliance health, doc counts + the full Add/Edit form
//   • PMProperty (detail) — rich multi-card Overview + Compliance / Tenancy
//     / Finance tabs, with an Edit action
// Reads properties / owners / property_owners / certificates, RLS-scoped.
// → window.PMPropertiesList, window.PMProperty
// ════════════════════════════════════════════════════════════════════════
(function () {
  const { useState, useEffect, useCallback, useRef } = React;

  const CERT_TYPES = [
    { v: 'gas_safety', l: 'Gas Safety (CP12)', req: true },
    { v: 'eicr', l: 'Electrical (EICR)', req: true },
    { v: 'epc', l: 'EPC', req: true },
    { v: 'deposit_protection', l: 'Deposit protection' },
    { v: 'right_to_rent', l: 'Right to Rent' },
    { v: 'hmo_licence', l: 'HMO licence' },
    { v: 'selective_licence', l: 'Selective licence' },
    { v: 'insurance', l: 'Insurance' },
  ];
  const LABEL = Object.fromEntries(CERT_TYPES.map(t => [t.v, t.l]));
  LABEL.tenancy_agreement = 'Tenancy agreement';
  LABEL.hmo_inspection = 'Inspection report';
  const CYCLE = { gas_safety: 12, eicr: 60, epc: 120, insurance: 12, hmo_licence: 60, selective_licence: 60 };
  const CORE = CERT_TYPES.filter(t => t.req).map(t => t.v);
  const PROP_TYPES = [['flat', 'Flat'], ['house', 'House'], ['hmo', 'HMO'], ['studio', 'Studio']];
  const BOROUGHS = [['Brent', 'Brent'], ['Harrow', 'Harrow'], ['Ealing', 'Ealing'], ['Camden', 'Camden'], ['Westminster', 'Westminster'], ['Other', 'Other']];
  const LICENCE = [['', '— select —'], ['none', 'No licence required'], ['selective', 'Selective licence'], ['hmo', 'HMO licence']];
  const LIC_LABEL = { none: 'No licence required', selective: 'Selective licence', hmo: 'HMO licence' };

  const fmt = (d) => d ? new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : '—';
  const money = (n) => (n == null || n === '') ? '—' : '£' + Number(n).toLocaleString('en-GB');
  const initials = (s) => (s || '?').trim().split(/\s+/).map(w => w[0]).slice(0, 2).join('').toUpperCase();
  const daysU = (d) => d ? Math.ceil((new Date(d) - new Date()) / 86400000) : null;
  function certStatus(expiry) {
    if (!expiry) return { cls: 'none', label: 'No date' };
    const d = daysU(expiry);
    if (d < 0) return { cls: 'bad', label: 'Expired' };
    if (d <= 60) return { cls: 'warn', label: 'Due ' + d + 'd' };
    return { cls: 'ok', label: 'Valid' };
  }
  // worst-of required-cert health for a property
  function health(certsForProp) {
    let crit = 0, warn = 0, ok = 0;
    CORE.forEach(type => {
      const list = certsForProp.filter(c => c.type === type && c.expiry_date).sort((a, b) => new Date(b.expiry_date) - new Date(a.expiry_date));
      const cert = list[0];
      if (!cert) { warn++; return; }
      const d = daysU(cert.expiry_date);
      if (d < 0) crit++; else if (d <= 60) warn++; else ok++;
    });
    return { h: crit ? 'crit' : warn ? 'warn' : 'ok', crit, warn, ok };
  }

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

  // ── Add / Edit property form (modal) ───────────────────────────────────
  function PropertyForm({ sb, account, owners, property, onClose, onSaved, toast, openBilling }) {
    const editing = !!property;
    const [f, setF] = useState(property ? {
      address: property.address || '', postcode: property.postcode || '', borough: property.borough || 'Brent',
      primary_owner_id: property.primary_owner_id || '', type: property.type || 'flat', licence_type: property.licence_type || '',
      beds: property.beds ?? '', baths: property.baths ?? '', rent: property.rent ?? '',
      occupants: property.occupants ?? '', permitted_occupants: property.permitted_occupants ?? '', licensing_reviewed: property.licensing_reviewed || '',
    } : { address: '', postcode: '', borough: 'Brent', primary_owner_id: (owners[0] || {}).id || '', type: 'flat', licence_type: '', beds: '', baths: '', rent: '', occupants: '', permitted_occupants: '', licensing_reviewed: '' });
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState('');
    const set = (k) => (e) => setF({ ...f, [k]: e.target.value });
    const setN = (k) => (e) => setF({ ...f, [k]: e.target.value.replace(/[^\d.]/g, '') });

    const save = async () => {
      setErr('');
      if (!f.address.trim()) return setErr('Address is required.');
      if (!f.postcode.trim()) return setErr('Postcode is required.');
      if (!f.licence_type) return setErr('Licensing status is required.');
      setBusy(true);
      const patch = {
        address: f.address.trim(), postcode: f.postcode.trim().toUpperCase(), borough: f.borough || null,
        primary_owner_id: f.primary_owner_id || null, type: f.type, licence_type: f.licence_type,
        beds: f.beds === '' ? null : Number(f.beds), baths: f.baths === '' ? null : Number(f.baths),
        rent: f.rent === '' ? null : Number(f.rent), occupants: f.occupants === '' ? null : Number(f.occupants),
        permitted_occupants: f.permitted_occupants === '' ? null : Number(f.permitted_occupants),
        licensing_reviewed: f.licensing_reviewed || null,
      };
      let error;
      if (editing) ({ error } = await sb.from('properties').update(patch).eq('id', property.id));
      else ({ error } = await sb.from('properties').insert({ account_id: account.id, ...patch }));
      setBusy(false);
      if (error) {
        if (/property_cap/.test(error.message)) { setErr('cap'); return; }
        return setErr(error.message);
      }
      toast(editing ? 'Property updated' : 'Property added'); onSaved();
    };

    return <div className="modal-scrim" onClick={onClose}>
      <div className="modal prop-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div><h2>{editing ? 'Edit property' : 'Add property'}</h2><p className="modal-sub">{editing ? 'Changes are saved to your portfolio.' : 'Add a property to start tracking its compliance, tenancy and finances.'}</p></div>
          <button className="x" onClick={onClose}>✕</button>
        </div>
        {err === 'cap' ? <div style={{ padding: '0 24px' }}><div className="alert alert-info"><span className="ic">🔒</span><div>You've reached your plan's property limit. {openBilling && <button className="linkbtn" onClick={openBilling}>Upgrade to add more →</button>}</div></div></div>
          : err && <div style={{ padding: '0 24px' }}><div className="alert alert-err"><span className="ic">⚠</span><div>{err}</div></div></div>}
        <div className="modal-form">
          <div className="field full"><label>Address</label><input value={f.address} onChange={set('address')} placeholder="14 Eldon Road" /></div>
          <div className="field"><label>Postcode</label><input value={f.postcode} onChange={set('postcode')} placeholder="HA1 5HU" /></div>
          <div className="field"><label>Borough</label><select value={f.borough} onChange={set('borough')}>{BOROUGHS.map(([k, v]) => <option key={k} value={k}>{v}</option>)}</select></div>
          <div className="field"><label>Primary owner</label><select value={f.primary_owner_id} onChange={set('primary_owner_id')}>{owners.length === 0 && <option value="">No owners yet</option>}{owners.map(o => <option key={o.id} value={o.id}>{o.name}</option>)}</select></div>
          <div className="field"><label>Type</label><select value={f.type} onChange={set('type')}>{PROP_TYPES.map(([k, v]) => <option key={k} value={k}>{v}</option>)}</select></div>
          <div className="field"><label>Licensing status *</label><select value={f.licence_type} onChange={set('licence_type')}>{LICENCE.map(([k, v]) => <option key={k} value={k}>{v}</option>)}</select></div>
          <div className="field"><label>Licensing reviewed</label><input type="date" value={f.licensing_reviewed} onChange={set('licensing_reviewed')} /></div>
          <div className="field"><label>Beds</label><input value={f.beds} onChange={setN('beds')} placeholder="3" /></div>
          <div className="field"><label>Baths</label><input value={f.baths} onChange={setN('baths')} placeholder="2" /></div>
          <div className="field"><label>Rent (£ pcm)</label><input value={f.rent} onChange={setN('rent')} placeholder="2450" /></div>
          <div className="field"><label>Current occupants</label><input value={f.occupants} onChange={setN('occupants')} placeholder="3" /></div>
          <div className="field"><label>Permitted occupants</label><input value={f.permitted_occupants} onChange={setN('permitted_occupants')} placeholder="4" /></div>
        </div>
        <div className="modal-foot" style={{ justifyContent: 'flex-start' }}>
          <button className="btn btn-primary" style={{ width: 'auto' }} onClick={save} disabled={busy}>{busy ? <Spin /> : (editing ? 'Save changes' : 'Add property')}</button>
          <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
        </div>
      </div>
    </div>;
  }

  // ── Properties list ────────────────────────────────────────────────────
  function PropertiesList({ sb, account, onOpen, toast, openBilling, initialOwner }) {
    const [props, setProps] = useState(null);
    const [owners, setOwners] = useState([]);
    const [jointMap, setJointMap] = useState({});   // property_id -> [owner_id]
    const [certs, setCerts] = useState([]);
    const [q, setQ] = useState('');
    const [ownerFilter, setOwnerFilter] = useState(initialOwner && initialOwner !== 'all' ? initialOwner : 'all');
    const [form, setForm] = useState(false);

    const load = useCallback(async () => {
      const [p, o, po, c] = await Promise.all([
        sb.from('properties').select('*').order('created_at', { ascending: true }),
        sb.from('owners').select('id,name,color,key'),
        sb.from('property_owners').select('property_id,owner_id'),
        sb.from('certificates').select('property_id,type,expiry_date'),
      ]);
      setProps(p.data || []); setOwners(o.data || []); setCerts(c.data || []);
      const jm = {}; (po.data || []).forEach(r => { (jm[r.property_id] = jm[r.property_id] || []).push(r.owner_id); });
      setJointMap(jm);
    }, [sb]);
    useEffect(() => { load(); }, [load]);

    const ownerById = Object.fromEntries(owners.map(o => [o.id, o]));
    const involvedOwners = (p) => {
      const ids = []; if (p.primary_owner_id) ids.push(p.primary_owner_id);
      (jointMap[p.id] || []).forEach(id => { if (!ids.includes(id)) ids.push(id); });
      return ids;
    };
    const certsFor = (id) => certs.filter(c => c.property_id === id);

    const filtered = (props || []).filter(p => {
      if (q && !((p.address || '') + ' ' + (p.postcode || '')).toLowerCase().includes(q.toLowerCase())) return false;
      if (ownerFilter !== 'all' && !involvedOwners(p).includes(ownerFilter)) return false;
      return true;
    });

    if (props === null) return <div className="card card-pad"><Spin /></div>;

    return <React.Fragment>
      <div className="pagehead" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, flexWrap: 'wrap' }}>
        <div><h1>Properties · {props.length}</h1><p>Click a property to open its detail. Add new properties manually below.</p></div>
        <button className="btn btn-primary btn-sm" onClick={() => setForm({ mode: 'new' })}>+ Add property</button>
      </div>

      <div className="pm-toolbar">
        <input className="pm-input pm-input-search" placeholder="Search by address or postcode…" value={q} onChange={e => setQ(e.target.value)} />
        <select className="pm-input" value={ownerFilter} onChange={e => setOwnerFilter(e.target.value)}>
          <option value="all">All owners</option>
          {owners.map(o => <option key={o.id} value={o.id}>{o.name}</option>)}
        </select>
        <span className="pmd-mono" style={{ fontSize: 12, color: 'var(--ink-faint)' }}>{filtered.length} shown</span>
      </div>

      <div className="card" style={{ overflow: 'hidden' }}>
        {filtered.length === 0 ? <div className="empty"><div className="ico">🏠</div><h3>{props.length === 0 ? 'Add your first property' : 'No matches'}</h3><p>{props.length === 0 ? 'Once it\'s in, PropMystro tracks its certificates, deadlines and finances.' : 'Try a different search or owner filter.'}</p>{props.length === 0 && <button className="btn btn-primary btn-sm" onClick={() => setForm({ mode: 'new' })}>+ Add a property</button>}</div>
        : <div style={{ overflowX: 'auto' }}><table className="prop-table">
          <thead><tr><th>Property</th><th>Owner(s)</th><th>Specs</th><th>Rent</th><th>Compliance</th><th>Docs</th><th></th></tr></thead>
          <tbody>
            {filtered.map(p => {
              const ids = involvedOwners(p); const hp = health(certsFor(p.id)); const docs = certsFor(p.id).length; const joint = ids.length > 1;
              return <tr key={p.id} className="clickable" onClick={() => onOpen(p)}>
                <td><div className="prop-addr">{p.address}</div><div className="prop-meta">{[p.borough, p.postcode].filter(Boolean).join(' · ')}</div></td>
                <td><span className="owner-stack">{ids.map(id => { const o = ownerById[id] || {}; return <span key={id} className="owner-mini" style={{ background: o.color || '#385a8a' }} title={o.name}>{(o.name || '?')[0]}</span>; })}{joint && <span className="joint-label">joint</span>}{ids.length === 0 && <span className="prop-meta">—</span>}</span></td>
                <td className="pmd-mono" style={{ fontSize: 12 }}>{(p.type || '').toUpperCase()}{p.beds != null ? ' · ' + p.beds + 'b' : ''}{p.baths != null ? '/' + p.baths + 'ba' : ''}</td>
                <td>{money(p.rent)}</td>
                <td><span className={'hdot ' + hp.h}></span><span className="pmd-mono" style={{ fontSize: 11, color: hp.h === 'crit' ? 'var(--red)' : hp.h === 'warn' ? 'var(--amber)' : 'var(--ok)' }}>{hp.h.toUpperCase()}</span></td>
                <td className="pmd-mono" style={{ fontSize: 12 }}>{docs}</td>
                <td><span style={{ color: 'var(--ink-faint)' }}>→</span></td>
              </tr>;
            })}
          </tbody>
        </table></div>}
      </div>

      {form && <PropertyForm sb={sb} account={account} owners={owners} property={null} onClose={() => setForm(false)} onSaved={() => { setForm(false); load(); }} toast={toast} openBilling={openBilling} />}
    </React.Fragment>;
  }

  // ── Property detail (rich overview + tabs) ─────────────────────────────
  const TYPE_LBL = { ast_periodic: 'AST · periodic', ast_fixed: 'AST · fixed', company_let: 'Company let', lodger: 'Lodger' };
  function monthsElapsed(start) { if (!start) return 0; const s = new Date(start), n = new Date(); let m = (n.getFullYear() - s.getFullYear()) * 12 + (n.getMonth() - s.getMonth()); if (n.getDate() >= s.getDate()) m += 1; return Math.max(0, m); }

  function PropertyDetail({ sb, property: initial, account, onBack, toast, openBilling }) {
    const [property, setProperty] = useState(initial);
    const [tab, setTab] = useState('overview');
    const [certs, setCerts] = useState(null);
    const [owners, setOwners] = useState([]);
    const [po, setPo] = useState([]);
    const [tenancy, setTenancy] = useState(null);
    const [household, setHousehold] = useState([]);
    const [payments, setPayments] = useState([]);
    const [expenses, setExpenses] = useState([]);
    const [tickets, setTickets] = useState([]);
    const [activity, setActivity] = useState([]);
    const [editing, setEditing] = useState(false);
    const [adding, setAdding] = useState(false);
    const Tenancy = window.PMTenancy, Finance = window.PMFinance, Maint = window.PMTabMaintenance;

    const load = useCallback(async () => {
      const [c, o, pol, pr, ty, pay, exp, tk, act] = await Promise.all([
        sb.from('certificates').select('*').eq('property_id', property.id).order('expiry_date', { ascending: true }),
        sb.from('owners').select('id,name,color'),
        sb.from('property_owners').select('property_id,owner_id,split_pct').eq('property_id', property.id),
        sb.from('properties').select('*').eq('id', property.id).maybeSingle(),
        sb.from('tenancies').select('*').eq('property_id', property.id).eq('status', 'active').order('start_date', { ascending: false }),
        sb.from('payments').select('amount,date').eq('property_id', property.id),
        sb.from('expenses').select('amount,date').eq('property_id', property.id),
        sb.from('tickets').select('*').eq('property_id', property.id).order('raised_at', { ascending: false }),
        sb.from('activity_log').select('*').eq('account_id', account.id).order('at', { ascending: false }),
      ]);
      setCerts(c.data || []); setOwners(o.data || []); setPo(pol.data || []);
      if (pr.data) setProperty(pr.data);
      const active = (ty.data || [])[0] || null; setTenancy(active);
      setPayments(pay.data || []); setExpenses(exp.data || []); setTickets(tk.data || []);
      setActivity((act.data || []).filter(a => !a.entity_id || a.entity_id === property.id || (a.details && JSON.stringify(a.details).includes(property.id))).slice(0, 30));
      if (active) { const hm = await sb.from('tenancy_members').select('tenant_id,relation,position').eq('tenancy_id', active.id); const ids = (hm.data || []).map(m => m.tenant_id); if (ids.length) { const tn = await sb.from('tenants').select('id,full_name').in('id', ids); const byId = Object.fromEntries((tn.data || []).map(t => [t.id, t])); setHousehold((hm.data || []).sort((a, b) => (a.position || 0) - (b.position || 0)).map(m => ({ ...m, tenant: byId[m.tenant_id] }))); } else setHousehold([]); }
      else setHousehold([]);
    }, [sb, property.id, account.id]);
    useEffect(() => { load(); }, [load]);

    const p = property;
    const ownerById = Object.fromEntries(owners.map(o => [o.id, o]));
    const ownerIds = (() => { const ids = []; if (p.primary_owner_id) ids.push(p.primary_owner_id); po.forEach(r => { if (!ids.includes(r.owner_id)) ids.push(r.owner_id); }); return ids; })();
    const isJoint = po.length > 1;
    const pctFor = (oid) => { const r = po.find(x => x.owner_id === oid); return r && r.split_pct != null ? Number(r.split_pct) : (isJoint ? Math.round(100 / po.length) : null); };
    const latestByType = (t) => (certs || []).filter(c => c.type === t && c.expiry_date).sort((a, b) => new Date(b.expiry_date) - new Date(a.expiry_date))[0];
    const hp = certs ? health(certs) : { h: 'ok', crit: 0, warn: 0, ok: 0 };
    const heroH = hp.crit ? 'crit' : hp.warn ? 'warn' : 'ok';
    const nextDue = (certs || []).filter(c => c.expiry_date).map(c => ({ c, d: daysU(c.expiry_date) })).filter(x => x.d != null && x.d >= -90).sort((a, b) => a.d - b.d)[0];
    const epc = latestByType('epc'); const epcBand = epc && epc.band ? String(epc.band).toUpperCase() : null;
    const occ = p.occupants != null ? Number(p.occupants) : null; const perm = p.permitted_occupants != null ? Number(p.permitted_occupants) : null;
    const scheme = LIC_LABEL[p.licence_type] || '—';
    const epcStatus = !epcBand ? null : ('ABCDEFG'.indexOf(epcBand) <= 2 ? 'ok' : epcBand === 'D' ? 'warn' : 'crit');
    // finance snapshot
    const yearAgo = new Date(Date.now() - 365 * 864e5);
    const collected = payments.filter(x => new Date(x.date) >= yearAgo).reduce((s, x) => s + Number(x.amount || 0), 0);
    const expenseTotal = expenses.filter(x => new Date(x.date) >= yearAgo).reduce((s, x) => s + Number(x.amount || 0), 0);
    const rentPcm = tenancy ? Number(tenancy.rent_pcm || 0) : 0;
    const received = payments.reduce((s, x) => s + Number(x.amount || 0), 0);
    const expected = tenancy ? rentPcm * monthsElapsed(tenancy.start_date) : 0;
    const arrearsBal = received - expected;
    // maintenance snapshot
    const openTickets = tickets.filter(t => t.status !== 'resolved');
    const agreementCert = (certs || []).find(c => c.type === 'tenancy_agreement');

    // open / download a stored original via a short-lived signed URL
    const openCert = async (c, download) => {
      if (!c.storage_path) return;
      const { data, error } = await sb.storage.from('documents').createSignedUrl(c.storage_path, 120, download ? { download: c.file_name || true } : undefined);
      if (error || !data) return toast('Could not open the file');
      window.open(data.signedUrl, '_blank');
    };

    const TABS = [
      { k: 'overview', l: 'Overview' },
      { k: 'tenancy', l: 'Tenancy' },
      { k: 'compliance', l: 'Compliance', badge: hp.crit || null, tone: 'crit' },
      { k: 'maintenance', l: 'Maintenance', badge: openTickets.length || null, tone: openTickets.some(t => t.priority === 'critical') ? 'crit' : 'plain' },
      { k: 'finance', l: 'Finance' },
      { k: 'documents', l: 'Documents', badge: certs ? certs.length || null : null },
      { k: 'activity', l: 'Activity', badge: activity.length || null },
    ];

    return <React.Fragment>
      <button className="backlink" onClick={onBack}>← Back to properties</button>

      {/* Hero */}
      <div className="prop-hero">
        <div className="prop-hero-l">
          <div className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)' }}>PROPERTY</div>
          <h1 style={{ fontSize: 26, margin: '2px 0 8px' }}>{p.address}</h1>
          <div className="prop-hero-meta">
            <span>{[p.borough, p.postcode].filter(Boolean).join(' · ')}</span>
            <span>{(p.beds ?? '—')} bed · {(p.baths ?? '—')} bath</span>
            <span className="pmd-mono prop-type-pill">{(p.type || '').toUpperCase()}</span>
            <span>{money(p.rent)}/mo</span>
          </div>
          <div className="prop-hero-owners">
            {ownerIds.map((id, i) => { const o = ownerById[id] || {}; const pct = pctFor(id); return <span key={id} className="owner-chip" style={{ background: (o.color || '#385a8a') + '22', borderColor: o.color || '#385a8a' }}>
              <span className="owner-dot" style={{ background: o.color || '#385a8a' }}></span>{o.name || 'Owner'}{pct != null && <span className="pmd-mono" style={{ marginLeft: 5, fontSize: 11 }}>{pct}%</span>}{i === 0 && isJoint && <span className="pm-primary-tag" style={{ marginLeft: 6 }}>primary</span>}
            </span>; })}
            {ownerIds.length === 0 && <span style={{ color: 'var(--ink-faint)', fontSize: 13 }}>No owner linked</span>}
          </div>
        </div>
        <div className="prop-hero-r">
          <div className={'health-card ' + heroH}><span className={'hdot ' + heroH}></span><strong>{heroH === 'crit' ? 'Critical' : heroH === 'warn' ? 'Warning' : 'Healthy'}</strong><div className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)' }}>{heroH === 'crit' ? 'Action needed' : heroH === 'warn' ? 'Expiring soon' : 'All current'}</div></div>
          <div style={{ display: 'flex', gap: 8, width: '100%' }}>
            <button className="btn btn-ghost btn-sm" style={{ flex: 1 }} onClick={() => setEditing(true)}>Edit details</button>
            <button className="btn btn-ghost btn-sm" style={{ flex: 1 }} onClick={() => setTab('documents')}>↑ Upload</button>
          </div>
        </div>
      </div>

      <div className="card" style={{ overflow: 'hidden' }}>
        <div className="tabbar prop-tabbar">
          {TABS.map(t => <button key={t.k} className={tab === t.k ? 'on' : ''} onClick={() => setTab(t.k)}>{t.l}{t.badge != null && <span className={'pm-tab-badge ' + (t.tone === 'crit' ? 'crit' : 'plain')}>{t.badge}</span>}</button>)}
        </div>

        {tab === 'finance' ? (Finance ? <Finance sb={sb} property={p} account={account} toast={toast} openBilling={openBilling} /> : null)
        : tab === 'tenancy' ? (Tenancy ? <Tenancy sb={sb} property={p} account={account} toast={toast} /> : null)
        : tab === 'maintenance' ? (Maint ? <Maint sb={sb} property={p} account={account} toast={toast} openBilling={openBilling} /> : <div className="empty" style={{ padding: '40px 20px' }}><div className="ico">🔧</div><h3>Maintenance</h3><p>{openTickets.length} open ticket{openTickets.length === 1 ? '' : 's'} for this property. The full maintenance board opens from the sidebar.</p></div>)
        : certs === null ? <div className="card-pad"><Spin /></div>
        : tab === 'documents' ? <React.Fragment>
          <div className="card-head"><div><h3>Documents</h3><div className="sub">{certs.length} certificate{certs.length === 1 ? '' : 's'} on file for this property</div></div></div>
          {certs.length === 0 ? <div className="empty"><div className="ico">📄</div><h3>No documents yet</h3><p>File a certificate on the Compliance tab, or use Documents → Upload &amp; file to auto-extract one.</p></div>
          : certs.map(c => { const s = c.type === 'tenancy_agreement' ? { cls: 'ok', label: 'Signed' } : certStatus(c.expiry_date); return <div className="row" key={c.id}>
            <span className="avatar" style={{ background: 'var(--surface-2)', color: 'var(--ink-faint)', fontSize: 10, fontWeight: 700 }}>{(LABEL[c.type] || '?').slice(0, 2).toUpperCase()}</span>
            <div className="main"><div className="t">{LABEL[c.type] || c.type}{c.cert_number ? ' · ' + c.cert_number : ''}</div><div className="s">{c.file_name ? c.file_name + ' · ' : ''}{c.type === 'tenancy_agreement' ? 'Generated ' + fmt(c.inspected_date) : 'Inspected ' + fmt(c.inspected_date) + ' · Expires ' + fmt(c.expiry_date)}</div></div>
            {c.storage_path ? <React.Fragment>
              <button className="btn btn-ghost btn-sm" onClick={() => openCert(c)}>View ↗</button>
              <button className="btn btn-ghost btn-sm" style={{ padding: '8px 10px' }} title="Download" onClick={() => openCert(c, true)}>↓</button>
            </React.Fragment> : <span className="pmd-mono" style={{ fontSize: 10.5, color: 'var(--ink-faint)' }}>no file</span>}
            <span className={'badge ' + s.cls}>{s.label}</span>
          </div>; })}
        </React.Fragment>
        : tab === 'activity' ? <React.Fragment>
          <div className="card-head"><div><h3>Activity</h3><div className="sub">Recent events for this property</div></div></div>
          {(() => { const events = [...activity.map(a => ({ at: a.at, msg: a.action + (a.details ? '' : '') })), ...certs.filter(c => c.created_at).map(c => ({ at: c.created_at, msg: 'Filed ' + (LABEL[c.type] || c.type) + (c.cert_number ? ' (' + c.cert_number + ')' : '') }))].sort((a, b) => (b.at || '').localeCompare(a.at || '')).slice(0, 20);
            return events.length === 0 ? <div className="empty"><div className="ico">🕑</div><h3>No activity yet</h3><p>Filing certificates, recording payments and raising tickets will appear here.</p></div>
            : <div style={{ padding: '4px 0' }}>{events.map((e, i) => <div className="row" key={i}><span className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)', minWidth: 130 }}>{new Date(e.at).toLocaleString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })}</span><div className="main"><div className="t" style={{ fontWeight: 500, fontSize: 13.5 }}>{e.msg}</div></div></div>)}</div>;
          })()}
        </React.Fragment>
        : tab === 'overview' ? <div className="card-pad">
          <div className="ov-grid">
            {/* Key details */}
            <div className="ov-card"><h4>Key details</h4><dl className="dl"><dt>Address</dt><dd>{p.address}</dd><dt>Postcode</dt><dd>{p.postcode}</dd><dt>Borough</dt><dd>{p.borough || '—'}</dd><dt>Type</dt><dd>{(p.type || '').toUpperCase()}</dd><dt>Beds / Baths</dt><dd>{(p.beds ?? '—')} / {(p.baths ?? '—')}</dd><dt>Rent</dt><dd>{money(p.rent)}/mo</dd></dl></div>

            {/* Compliance */}
            <div className="ov-card ov-link" onClick={() => setTab('compliance')}>
              <h4>Compliance <span className="ov-go">View →</span></h4>
              <div className="mini-kpis"><div className={'mini-kpi' + (hp.crit ? ' crit' : '')}><span className="n">{hp.crit}</span><span className="l">critical</span></div><div className={'mini-kpi' + (hp.warn ? ' warn' : '')}><span className="n">{hp.warn}</span><span className="l">warning</span></div><div className={'mini-kpi' + (hp.ok ? ' ok' : '')}><span className="n">{hp.ok}</span><span className="l">healthy</span></div></div>
              {nextDue && <div className="ov-row"><span className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)' }}>NEXT DUE</span> <strong>{LABEL[nextDue.c.type] || nextDue.c.type}</strong> <span className={'badge ' + certStatus(nextDue.c.expiry_date).cls}>{nextDue.d < 0 ? Math.abs(nextDue.d) + 'd overdue' : nextDue.d + 'd'}</span></div>}
            </div>

            {/* Licensing */}
            <div className="ov-card"><h4>Licensing</h4>
              <div style={{ marginBottom: 8 }}><strong>{scheme}</strong>{p.borough ? <span style={{ color: 'var(--ink-faint)' }}> · {p.borough}</span> : ''}</div>
              {p.licence_type === 'none' ? <div style={{ color: 'var(--ink-faint)', fontSize: 13 }}>No property licence required.</div>
                : <div className="ov-row" style={{ marginBottom: 8 }}><span className="pmd-mono" style={{ fontSize: 11 }}>{(LABEL.hmo_licence).toUpperCase()}</span> {latestByType('hmo_licence') ? <span className={'badge ' + certStatus(latestByType('hmo_licence').expiry_date).cls}>{certStatus(latestByType('hmo_licence').expiry_date).label}</span> : <span className="badge none">Not on file</span>}</div>}
              <div className="ov-row"><span className="pmd-mono" style={{ fontSize: 10, color: 'var(--ink-faint)' }}>OCCUPANTS</span> <span>{occ != null ? occ : '—'}{perm != null ? ' of ' + perm + ' permitted' : ''}</span></div>
              {occ != null && perm != null && <div className="occ-bar"><div className="occ-fill" style={{ width: Math.min(100, Math.round(occ / Math.max(perm, 1) * 100)) + '%' }}></div></div>}
              <div className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)', marginTop: 6 }}>{occ != null && occ >= 5 ? 'Over HMO threshold (5+)' : 'Under HMO threshold'}{p.licensing_reviewed ? ' · reviewed ' + fmt(p.licensing_reviewed) : ''}</div>
            </div>

            {/* Asset health · EPC */}
            <div className="ov-card"><h4>Asset health <span className="ov-go">EPC</span></h4>
              {epcBand ? <React.Fragment><div style={{ marginBottom: 6 }}><strong>Band {epcBand}</strong><span style={{ color: 'var(--ink-faint)' }}> · target C by 2030</span></div>
                <div className="epc-scale">{['A', 'B', 'C', 'D', 'E', 'F', 'G'].map(b => <div key={b} className={'epc-cell' + (b === epcBand ? ' cur ' + epcStatus : '') + (b === 'C' ? ' target' : '')}>{b}</div>)}</div>
                {epcStatus !== 'ok' && <div className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)', marginTop: 8 }}>Below target — retrofit likely needed to reach C by 2030</div>}
              </React.Fragment> : <div style={{ color: 'var(--ink-faint)', fontSize: 13 }}>No EPC on file — add one on the Compliance tab.</div>}
            </div>

            {/* Tenancy */}
            <div className="ov-card ov-link" onClick={() => setTab('tenancy')}><h4>Tenancy <span className="ov-go">View →</span></h4>
              {tenancy ? <dl className="dl"><dt>Current tenant</dt><dd>{household[0] && household[0].tenant ? household[0].tenant.full_name : '—'}{household.length > 1 ? ' +' + (household.length - 1) : ''}</dd><dt>Type</dt><dd>{TYPE_LBL[tenancy.type] || tenancy.type || '—'}</dd><dt>Start</dt><dd>{fmt(tenancy.start_date)}</dd><dt>Rent</dt><dd>{money(tenancy.rent_pcm)}/mo</dd><dt>Deposit</dt><dd>{Number(tenancy.deposit_amount) ? money(tenancy.deposit_amount) + ' · ' + (tenancy.deposit_scheme || '—') : 'none'}</dd><dt>Agreement</dt><dd>{agreementCert ? <span style={{ color: 'var(--ok)' }}>✓ signed</span> : <span style={{ color: 'var(--amber)' }}>not on file</span>}</dd></dl>
                : <div style={{ color: 'var(--ink-faint)', fontSize: 13 }}>No active tenancy · vacant.<div className="pmd-mono" style={{ fontSize: 11, marginTop: 6 }}>Record a tenancy in the Tenancy tab.</div></div>}
            </div>

            {/* Finance */}
            <div className="ov-card ov-link" onClick={() => setTab('finance')}><h4>Finance <span className="ov-go">View →</span></h4>
              {tenancy ? <dl className="dl"><dt>Rent</dt><dd>{money(rentPcm)}/mo</dd><dt>Arrears</dt><dd>{arrearsBal < 0 ? <span style={{ color: 'var(--red)' }}>{money(-arrearsBal)} owed</span> : <span style={{ color: 'var(--ok)' }}>Up to date</span>}</dd><dt>Collected 12mo</dt><dd>{money(collected)}</dd><dt>Expenses 12mo</dt><dd>{money(expenseTotal)}</dd></dl>
                : <div style={{ color: 'var(--ink-faint)', fontSize: 13 }}>No active tenancy · no rent ledger.</div>}
            </div>

            {/* Maintenance */}
            <div className="ov-card ov-link" onClick={() => setTab('maintenance')}><h4>Maintenance <span className="ov-go">View →</span></h4>
              <div className="doc-snap"><strong style={openTickets.some(t => t.priority === 'critical') ? { color: 'var(--red)' } : null}>{openTickets.length}</strong><span style={{ color: 'var(--ink-faint)' }}> open ticket{openTickets.length === 1 ? '' : 's'}</span></div>
              <ul className="doc-list">{openTickets.slice(0, 3).map(t => <li key={t.id}>{(t.title || '').length > 38 ? t.title.slice(0, 36) + '…' : t.title} <span className="pmd-mono" style={{ color: 'var(--ink-faint)' }}>· {t.status}</span></li>)}{openTickets.length === 0 && <li style={{ color: 'var(--ink-faint)' }}>{tickets[0] ? 'No open tickets · last ' + fmt(tickets[0].raised_at) : 'No maintenance tickets yet'}</li>}</ul>
            </div>

            {/* Documents */}
            <div className="ov-card ov-link" onClick={() => setTab('documents')}><h4>Documents <span className="ov-go">View →</span></h4>
              <div className="doc-snap"><strong>{certs.length}</strong><span style={{ color: 'var(--ink-faint)' }}> certificates on file</span></div>
              <ul className="doc-list">{certs.slice(0, 4).map(c => <li key={c.id}><span className="pmd-mono">{(LABEL[c.type] || c.type).split(' ')[0].toUpperCase()}</span> {c.cert_number || 'unnumbered'}</li>)}{certs.length === 0 && <li style={{ color: 'var(--ink-faint)' }}>No documents yet</li>}</ul>
            </div>

            {/* Recent activity (wide) */}
            <div className="ov-card ov-wide ov-link" onClick={() => setTab('activity')}><h4>Recent activity <span className="ov-go">View all →</span></h4>
              {(() => { const events = [...certs.filter(c => c.created_at).map(c => ({ at: c.created_at, msg: 'Filed ' + (LABEL[c.type] || c.type) + (c.cert_number ? ' (' + c.cert_number + ')' : '') })), ...activity.map(a => ({ at: a.at, msg: a.action }))].sort((a, b) => (b.at || '').localeCompare(a.at || '')).slice(0, 5);
                return events.length === 0 ? <div style={{ color: 'var(--ink-faint)', fontSize: 13 }}>No activity recorded yet.</div>
                : <ul className="act-list">{events.map((e, i) => <li key={i}><span className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)' }}>{new Date(e.at).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })}</span> <span>{e.msg}</span></li>)}</ul>;
              })()}
            </div>
          </div>
        </div>
        : /* compliance */ <React.Fragment>
          <div className="card-pad" style={{ borderBottom: '1px solid var(--line-soft)' }}>
            <div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--ink-faint)', textTransform: 'uppercase', letterSpacing: '.05em', marginBottom: 12 }}>Core certificates</div>
            {CORE.map(t => { const c = latestByType(t); const s = certStatus(c?.expiry_date); return <div className="kv" key={t} style={{ alignItems: 'center' }}>
              <span className="k" style={{ width: 170 }}>{LABEL[t]}</span>
              <span className="v" style={{ flex: 1 }}>{c ? 'Expires ' + fmt(c.expiry_date) : <span style={{ color: 'var(--ink-faint)', fontWeight: 400 }}>Not recorded</span>}</span>
              {c ? <span className={'badge ' + s.cls}>{s.label}</span> : <span className="badge none">Missing</span>}
            </div>; })}
          </div>
          <div className="card-head"><div><h3>All certificates</h3><div className="sub">{certs.length} on file</div></div><button className="btn btn-ghost btn-sm" onClick={() => setAdding(true)}>+ Add certificate</button></div>
          {certs.length === 0 && !adding && <div className="empty"><div className="ico">📋</div><h3>No certificates yet</h3><p>Drop gas, electrical and EPC certificates — the details are read automatically and PropMystro tracks their expiry for you.</p><button className="btn btn-primary btn-sm" onClick={() => setAdding(true)}>+ Add certificate</button></div>}
          {certs.map(c => { const s = certStatus(c.expiry_date); return <div className="row" key={c.id}>
            <span className="avatar" style={{ background: s.cls === 'bad' ? 'var(--red-soft)' : s.cls === 'warn' ? 'var(--amber-soft)' : 'var(--ok-soft)' }}>{(LABEL[c.type] || '?')[0]}</span>
            <div className="main"><div className="t">{LABEL[c.type] || c.type}{c.cert_number ? ' · ' + c.cert_number : ''}</div><div className="s">Inspected {fmt(c.inspected_date)} · Expires {fmt(c.expiry_date)}</div></div>
            {c.storage_path && <button className="btn btn-ghost btn-sm" onClick={() => openCert(c)}>View ↗</button>}
            <span className={'badge ' + s.cls}>{s.label}</span>
          </div>; })}
        </React.Fragment>}
      </div>

      {editing && <PropertyForm sb={sb} account={account} owners={owners} property={p} onClose={() => setEditing(false)} onSaved={() => { setEditing(false); load(); }} toast={toast} openBilling={openBilling} />}
      {adding && <CertDropModal sb={sb} account={account} property={p} onClose={() => setAdding(false)} onSaved={() => { setAdding(false); load(); }} toast={toast} />}
    </React.Fragment>;
  }

  // ── drop-a-certificate modal (Compliance tab) — extract → review → file ──
  function CertDropModal({ sb, account, property, onClose, onSaved, toast }) {
    const fileRef = useRef(null);
    const [file, setFile] = useState(null);   // { raw, name, mimeType }
    const [over, setOver] = useState(false);
    const [extracting, setExtracting] = useState(false);
    const [note, setNote] = useState('');
    const [err, setErr] = useState('');
    const [busy, setBusy] = useState(false);
    const [f, setF] = useState({ type: 'gas_safety', cert_number: '', inspected_date: '', expiry_date: '', result: '' });
    const set = (patch) => setF(s => ({ ...s, ...patch }));

    const onFile = async (raw) => {
      if (!raw) return;
      setErr(''); setNote('');
      if (raw.size > 8 * 1048576) return setErr('Keep files under 8 MB.');
      setFile({ raw, name: raw.name, mimeType: raw.type });
      setExtracting(true);
      try {
        const base64 = await new Promise((res, rej) => { const r = new FileReader(); r.onload = () => { const s = String(r.result || ''); const i = s.indexOf(','); res(i >= 0 ? s.slice(i + 1) : s); }; r.onerror = () => rej(new Error('Could not read file')); r.readAsDataURL(raw); });
        const { data, error } = await sb.functions.invoke('extract-document', { body: { base64, mimeType: raw.type } });
        const ex = data && data.extracted;
        if (error || !ex) throw new Error((data && data.error) || (error && error.message) || 'no result');
        const patch = {};
        if (ex.documentType && CERT_TYPES.some(t => t.v === ex.documentType)) patch.type = ex.documentType;
        if (ex.certificateNumber) patch.cert_number = ex.certificateNumber;
        if (ex.inspectedDate) patch.inspected_date = ex.inspectedDate;
        if (ex.expiryDate) patch.expiry_date = ex.expiryDate;
        if (ex.result || ex.band) patch.result = ex.result || ex.band;
        set(patch);
        setNote('Read automatically — check the fields before filing.');
      } catch (e) {
        setNote('Couldn\u2019t read it automatically — fill the details in below.');
      } finally { setExtracting(false); }
    };

    const save = async () => {
      setErr('');
      if (!f.type) return setErr('Choose a certificate type.');
      setBusy(true);
      let storage_path = null;
      if (file) {
        const safe = file.name.replace(/[^\w.\-]+/g, '_');
        storage_path = account.id + '/' + property.id + '/' + Date.now() + '-' + safe;
        const up = await sb.storage.from('documents').upload(storage_path, file.raw, { contentType: file.mimeType || undefined });
        if (up.error) { setBusy(false); return setErr('Upload failed: ' + up.error.message); }
      }
      let expiry = f.expiry_date || null;
      if (!expiry && f.inspected_date && CYCLE[f.type]) { const dd = new Date(f.inspected_date); if (!isNaN(dd)) { dd.setMonth(dd.getMonth() + CYCLE[f.type]); expiry = dd.toISOString().slice(0, 10); } }
      const { error } = await sb.from('certificates').insert({ property_id: property.id, type: f.type, cert_number: f.cert_number.trim() || null, inspected_date: f.inspected_date || null, expiry_date: expiry, result: f.result.trim() || null, storage_path, file_name: file ? file.name : null });
      if (error) { setBusy(false); return setErr(error.message); }
      if (file && storage_path) await sb.from('documents').insert({ filename: file.name, storage_path, mime_type: file.mimeType || null, category: 'compliance', property_id: property.id });
      setBusy(false);
      toast('Filed ' + (LABEL[f.type] || 'certificate'));
      onSaved();
    };

    return <div className="modal-scrim" onClick={onClose}><div className="modal" style={{ maxWidth: 620 }} onClick={e => e.stopPropagation()}>
      <div className="modal-head"><div><div className="pmd-mono" style={{ fontSize: 11, color: 'var(--brand-deep)' }}>COMPLIANCE · {property.address}</div><h2>Add a certificate</h2><p className="modal-sub">Drop the certificate — the type, dates and reference are read for you, and the original is filed under this property.</p></div><button className="x" onClick={onClose}>✕</button></div>
      {err && <div style={{ padding: '0 24px' }}><div className="alert alert-err"><span className="ic">⚠</span><div>{err}</div></div></div>}
      <div style={{ padding: '0 24px' }}>
        <div className={'dropzone' + (over ? ' on' : '')} onClick={() => fileRef.current && fileRef.current.click()}
          onDragOver={e => { e.preventDefault(); setOver(true); }} onDragLeave={() => setOver(false)}
          onDrop={e => { e.preventDefault(); setOver(false); onFile(e.dataTransfer.files[0]); }}>
          <input ref={fileRef} type="file" accept=".pdf,image/*" style={{ display: 'none' }} onChange={e => { onFile(e.target.files[0]); e.target.value = ''; }} />
          {file ? <React.Fragment><div className="dz-icon">{extracting ? '⏳' : '✓'}</div><div className="dz-h">{file.name}</div><div className="dz-s">{extracting ? 'Reading the certificate…' : (note || 'drop or click to replace')}</div></React.Fragment>
            : <React.Fragment><div className="dz-icon">↑</div><div className="dz-h">Drag &amp; drop the certificate, or click to browse</div><div className="dz-s">PDF or photo · CP12 · EICR · EPC · licence · insurance</div></React.Fragment>}
        </div>
      </div>
      <div className="modal-form" style={{ marginTop: 14 }}>
        <div className="field"><label>Type</label><select value={f.type} onChange={e => set({ type: e.target.value })}>{CERT_TYPES.map(t => <option key={t.v} value={t.v}>{t.l}</option>)}</select></div>
        <div className="field"><label>Certificate no.</label><input value={f.cert_number} onChange={e => set({ cert_number: e.target.value })} placeholder="optional" /></div>
        <div className="field"><label>Inspected</label><input type="date" value={f.inspected_date} onChange={e => set({ inspected_date: e.target.value })} /></div>
        <div className="field"><label>Expires</label><input type="date" value={f.expiry_date} onChange={e => set({ expiry_date: e.target.value })} /></div>
        <div className="field full"><label>Result / band</label><input value={f.result} onChange={e => set({ result: e.target.value })} placeholder="e.g. PASS · SATISFACTORY · C" /></div>
      </div>
      {!f.expiry_date && f.inspected_date && CYCLE[f.type] && <div style={{ padding: '6px 24px 0' }}><span className="pmd-mono" style={{ fontSize: 11, color: 'var(--ink-faint)' }}>Expiry auto-computes: inspected + {CYCLE[f.type]} months</span></div>}
      <div className="modal-foot" style={{ justifyContent: 'flex-end', gap: 8 }}>
        <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
        <button className="btn btn-primary" style={{ width: 'auto' }} disabled={busy || extracting} onClick={save}>{busy ? <Spin /> : 'File this certificate'}</button>
      </div>
    </div></div>;
  }

  window.PMPropertiesList = PropertiesList;
  window.PMProperty = PropertyDetail;
})();
