// ════════════════════════════════════════════════════════════════════════
// PropMystro · Compliance Shield (matrix) · pm-d-compliance.jsx
// The signature at-a-glance grid from the Apps Script app: every property
// (rows) × every required certificate (columns), traffic-light cells with a
// days-to-expiry label, portfolio KPIs, owner column, next-due, search +
// owner filter. Click any cell/row to open the property. Reads properties /
// owners / property_owners / certificates, RLS-scoped. → window.PMCompliance
// ════════════════════════════════════════════════════════════════════════
(function () {
  const { useState, useEffect, useCallback } = React;

  // required certs shown as columns (hmo_licence only applies to HMOs)
  const COLS = [
    { v: 'gas_safety', s: 'Gas', cycle: 12 },
    { v: 'eicr', s: 'EICR', cycle: 60 },
    { v: 'epc', s: 'EPC', cycle: 120 },
    { v: 'deposit_protection', s: 'Deposit' },
    { v: 'right_to_rent', s: 'RTR' },
    { v: 'insurance', s: 'Ins', cycle: 12 },
    { v: 'hmo_licence', s: 'HMO', hmoOnly: true, cycle: 60 },
  ];
  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;
  const applies = (col, p) => col.hmoOnly ? (p.type === 'hmo') : true;
  function cellState(cert) {
    if (!cert) return { st: 'missing', lbl: '—' };
    const d = daysU(cert.expiry_date);
    if (d == null) return { st: 'ok', lbl: '✓' };
    if (d < 0) return { st: 'crit', lbl: Math.abs(d) + 'd over' };
    if (d <= 60) return { st: 'warn', lbl: d + 'd' };
    if (d < 365) return { st: 'ok', lbl: d + 'd' };
    return { st: 'ok', lbl: Math.floor(d / 30) + 'mo' };
  }

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

  function Compliance({ sb, onOpenProperty }) {
    const [props, setProps] = useState(null);
    const [owners, setOwners] = useState([]);
    const [po, setPo] = useState([]);
    const [certs, setCerts] = useState([]);
    const [q, setQ] = useState('');
    const [ownerFilter, setOwnerFilter] = useState('all');

    const load = useCallback(async () => {
      const [p, o, pol, c] = await Promise.all([
        sb.from('properties').select('id,address,postcode,borough,type,primary_owner_id').order('address', { ascending: true }),
        sb.from('owners').select('id,name,color'),
        sb.from('property_owners').select('property_id,owner_id'),
        sb.from('certificates').select('property_id,type,expiry_date,engineer_name'),
      ]);
      setProps(p.data || []); setOwners(o.data || []); setPo(pol.data || []); setCerts(c.data || []);
    }, [sb]);
    useEffect(() => { load(); }, [load]);

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

    const ownerById = Object.fromEntries(owners.map(o => [o.id, o]));
    const involved = (p) => { const ids = []; if (p.primary_owner_id) ids.push(p.primary_owner_id); po.filter(r => r.property_id === p.id).forEach(r => { if (!ids.includes(r.owner_id)) ids.push(r.owner_id); }); return ids; };
    const latest = (pid, type) => certs.filter(c => c.property_id === pid && c.type === type && true).sort((a, b) => new Date(b.expiry_date || 0) - new Date(a.expiry_date || 0))[0];

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

    // portfolio KPIs across applicable required cells
    let compliant = 0, expiring = 0, expired = 0, missing = 0, nextDue = null;
    filtered.forEach(p => COLS.forEach(col => {
      if (!applies(col, p)) return;
      const cell = cellState(latest(p.id, col.v));
      if (cell.st === 'missing') missing++; else if (cell.st === 'crit') expired++; else if (cell.st === 'warn') expiring++; else compliant++;
      const c = latest(p.id, col.v); const d = c ? daysU(c.expiry_date) : null;
      if (d != null && d >= 0 && (!nextDue || d < nextDue.d)) nextDue = { d, p, col, c };
    }));

    return <React.Fragment>
      <div className="pagehead"><h1>Compliance</h1><p>Live compliance for every property × every required certificate. Click a cell to open the property.</p></div>

      <div className="summary">
        <div className="sumcard ok"><div className="n">{compliant}</div><div className="l">Compliant</div></div>
        <div className="sumcard warn"><div className="n">{expiring}</div><div className="l">Due ≤60 days</div></div>
        <div className="sumcard bad"><div className="n">{expired}</div><div className="l">Expired</div></div>
        <div className="sumcard"><div className="n">{missing}</div><div className="l">Missing</div></div>
      </div>

      <div className="pm-toolbar" style={{ marginTop: 16 }}>
        <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} {filtered.length === 1 ? 'property' : 'properties'}</span>
      </div>

      <div className="card" style={{ overflow: 'hidden' }}>
        {filtered.length === 0 ? <div className="empty"><div className="ico">🛡️</div><h3>{props.length === 0 ? 'No properties yet' : 'No matches'}</h3><p>{props.length === 0 ? 'Add a property to start tracking compliance.' : 'Try a different search or owner filter.'}</p></div>
        : <div className="matrix-wrap"><table className="matrix">
          <thead><tr>
            <th className="mx-prop">Property</th><th className="mx-owner">Owner</th>
            {COLS.map(c => <th key={c.v} className="mx-cell-h" title={c.v}>{c.s}</th>)}
            <th className="mx-next">Next due</th>
          </tr></thead>
          <tbody>
            {filtered.map(p => {
              const ids = involved(p);
              // per-row next due
              let rn = null;
              COLS.forEach(col => { if (!applies(col, p)) return; const c = latest(p.id, col.v); const d = c ? daysU(c.expiry_date) : null; if (d != null && (!rn || d < rn.d)) rn = { d, col }; });
              return <tr key={p.id} className="clickable" onClick={() => onOpenProperty && onOpenProperty(p)}>
                <td><div className="prop-addr">{p.address}</div><div className="prop-meta">{[p.borough, p.postcode].filter(Boolean).join(' · ')}{p.type === 'hmo' ? ' · HMO' : ''}</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>; })}{ids.length === 0 && <span className="prop-meta">—</span>}</span></td>
                {COLS.map(col => {
                  if (!applies(col, p)) return <td key={col.v} className="mx-cell na"><span className="mx-lbl">n/a</span></td>;
                  const cell = cellState(latest(p.id, col.v));
                  return <td key={col.v} className={'mx-cell ' + cell.st}><span className="mx-dot"></span><span className="mx-lbl">{cell.lbl}</span></td>;
                })}
                <td className="pmd-mono" style={{ fontSize: 11 }}>{rn ? (rn.d < 0 ? <span style={{ color: 'var(--red)' }}>{rn.col.s} overdue</span> : <span>{rn.col.s} · {rn.d}d</span>) : '—'}</td>
              </tr>;
            })}
          </tbody>
        </table></div>}
      </div>

      <div className="mx-legend">
        <span><span className="mx-dot ok-d"></span> Valid</span>
        <span><span className="mx-dot warn-d"></span> Due ≤60d</span>
        <span><span className="mx-dot crit-d"></span> Expired</span>
        <span><span className="mx-dot miss-d"></span> Missing</span>
        <span className="pmd-mono" style={{ marginLeft: 'auto', color: 'var(--ink-faint)' }}>{nextDue ? 'Next portfolio event: ' + nextDue.col.s + ' at ' + nextDue.p.address + ' · ' + nextDue.d + 'd' : 'All clear'}</span>
      </div>
    </React.Fragment>;
  }

  window.PMCompliance = Compliance;
})();
