// ════════════════════════════════════════════════════════════════════════
// PropMystro · Phase C · pm-c-app.jsx
// Working auth + onboarding prototype, wired to a live Supabase project.
//   • Connect once (project URL + anon public key — saved in this browser)
//   • Create account  → handle_new_user trigger spins up an account + admin
//   • Sign in / forgot password
//   • Onboarding: add your first property (RLS-scoped, cap-checked)
//   • Home: account + plan/trial, your properties, your team + invite RPC
// Everything here exercises the real Phase B database.
// ════════════════════════════════════════════════════════════════════════
const { useState, useEffect, useCallback, useRef } = React;

const LS = { url: 'pm_url', anon: 'pm_anon' };
// Baked-in public config (set in index.html's window.PM_PUBLIC) takes priority;
// localStorage is the dev fallback so the Connect gate never shows in production.
const PUB = (typeof window !== 'undefined' && window.PM_PUBLIC) || {};
const cfgUrl = () => ((PUB.SUPABASE_URL || '').trim().replace(/\/$/, '')) || localStorage.getItem(LS.url) || '';
const cfgAnon = () => ((PUB.SUPABASE_ANON || '').trim()) || localStorage.getItem(LS.anon) || '';
const ROLES = [
  { v: 'family_owner', l: 'Co-owner' },
  { v: 'accountant',   l: 'Accountant' },
  { v: 'inspector',    l: 'Inspector' },
  { v: 'contractor',   l: 'Contractor' },
];
const ROLE_LABEL = {
  family_admin: 'Admin', family_owner: 'Co-owner', accountant: 'Accountant',
  inspector: 'Inspector', contractor: 'Contractor', tenant: 'Tenant',
};
const PROP_TYPES = [
  { v: 'flat', l: 'Flat' }, { v: 'house', l: 'House' },
  { v: 'hmo', l: 'HMO' }, { v: 'studio', l: 'Studio' },
];

const initials = (s) => (s || '?').trim().split(/\s+/).map(w => w[0]).slice(0, 2).join('').toUpperCase();
const daysLeft = (iso) => { if (!iso) return null; const d = Math.ceil((new Date(iso) - new Date()) / 86400000); return d; };

// ── tiny shared bits ──────────────────────────────────────────────────────
function Spinner({ dark }) { return <span className={'spin' + (dark ? ' dark' : '')} />; }

function Alert({ kind, children }) {
  if (!children) return null;
  const ic = kind === 'err' ? '⚠' : kind === 'ok' ? '✓' : 'ℹ';
  return <div className={'alert alert-' + (kind === 'err' ? 'err' : kind === 'ok' ? 'ok' : 'info')}>
    <span className="ic">{ic}</span><div>{children}</div>
  </div>;
}

function Wordmark({ light, lg }) {
  return <span className={'wm' + (light ? ' light' : '') + (lg ? ' lg' : '')}>
    <span className="mark" /> PropMystro
  </span>;
}

// ════════════════════════════════════════════════════════════════════════
// CONNECT GATE — one-time: paste the same two values from the setup test.
// ════════════════════════════════════════════════════════════════════════
function ConnectGate({ onConnect }) {
  const [url, setUrl] = useState('');
  const [anon, setAnon] = useState('');
  const [err, setErr] = useState('');

  const go = () => {
    const u = url.trim().replace(/\/$/, '');
    if (!/^https:\/\/.+\.supabase\.co$/.test(u)) return setErr('That project URL doesn\'t look right — it should look like https://abcd1234.supabase.co');
    if (anon.trim().length < 30) return setErr('That anon key looks too short — paste the full “anon public” key.');
    localStorage.setItem(LS.url, u);
    localStorage.setItem(LS.anon, anon.trim());
    onConnect();
  };

  return <div className="connect">
    <div className="connect-card">
      <Wordmark />
      <h2>Connect your workspace</h2>
      <p className="lede">A one-time step for this prototype. Paste the same two values you used in the Phase B safety test — they're saved in this browser only.</p>
      <Alert kind="err">{err}</Alert>
      <div className="field">
        <label>Project URL</label>
        <input value={url} onChange={e => setUrl(e.target.value)} placeholder="https://abcd1234.supabase.co" spellCheck="false" />
      </div>
      <div className="field">
        <label>anon public key</label>
        <input value={anon} onChange={e => setAnon(e.target.value)} placeholder="eyJhbGciOi…" spellCheck="false" />
      </div>
      <button className="btn btn-primary" onClick={go}>Connect →</button>
      <div className="connect-note"><b>Where to find these:</b> Supabase → Project Settings (gear) → API → “Project URL” and “Project API keys → anon public”. Never paste the <b>service_role</b> / secret key here.</div>
    </div>
  </div>;
}

// ════════════════════════════════════════════════════════════════════════
// AUTH — sign in / create account / forgot password
// ════════════════════════════════════════════════════════════════════════
function AuthScreen({ sb }) {
  const [tab, setTab] = useState('signin');   // signin | signup
  const [company, setCompany] = useState('');
  const [email, setEmail] = useState('');
  const [pw, setPw] = useState('');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [ok, setOk] = useState('');

  const reset = () => { setErr(''); setOk(''); };

  const signin = async () => {
    reset(); setBusy(true);
    const { error } = await sb.auth.signInWithPassword({ email: email.trim(), password: pw });
    setBusy(false);
    if (error) setErr(error.message);
    // success → onAuthStateChange in App swaps the view
  };

  const signup = async () => {
    reset();
    if (!company.trim()) return setErr('Give your portfolio or company a name.');
    if (pw.length < 6) return setErr('Use a password of at least 6 characters.');
    setBusy(true);
    const { data, error } = await sb.auth.signUp({
      email: email.trim(), password: pw,
      options: { data: { company: company.trim() }, emailRedirectTo: location.origin + location.pathname },
    });
    setBusy(false);
    if (error) return setErr(error.message);
    if (!data.session) {
      setOk('Account created! We’ve emailed you a confirmation link — click it to verify your address, then you’ll be signed straight in. (Check your spam folder if it doesn’t arrive within a minute.)');
      setTab('signin');
    }
    // if session present, App's listener takes over
  };

  const forgot = async () => {
    reset();
    if (!email.trim()) return setErr('Enter your email first, then tap “Forgot?”.');
    const { error } = await sb.auth.resetPasswordForEmail(email.trim());
    if (error) setErr(error.message); else setOk('Password reset link sent — check your email.');
  };

  return <div className="auth">
    <aside className="auth-aside">
      <div className="glow" /><div className="glow2" />
      <Wordmark light />
      <div className="aside-body">
        <div className="eyebrow">Everything landlords juggle — one platform</div>
        <h1>Compliant. Paid on time. Tax-ready.</h1>
        <p>PropMystro runs your whole portfolio — 3 properties or 300 — from legal compliance and tenancy paperwork to rent, arrears, maintenance and Making Tax Digital. It watches every deadline and tells you what needs attention before it's late.</p>
        <ul className="aside-list">
          <li><span className="tick">✓</span><span>Compliance, certificates &amp; legal notices — never miss a renewal</span></li>
          <li><span className="tick">✓</span><span>Rent, arrears, expenses &amp; tax-ready SA105 in one ledger</span></li>
          <li><span className="tick">✓</span><span>Maintenance, inspections &amp; tenant messaging that keep you onside</span></li>
        </ul>
      </div>
      <div className="aside-foot">14-day free trial · no card required</div>
    </aside>

    <main className="auth-main">
      <div className="auth-card">
        <div className="topwm"><Wordmark /></div>
        <div className="tabs">
          <button className={tab === 'signin' ? 'on' : ''} onClick={() => { setTab('signin'); reset(); }}>Sign in</button>
          <button className={tab === 'signup' ? 'on' : ''} onClick={() => { setTab('signup'); reset(); }}>Create account</button>
        </div>

        <Alert kind="err">{err}</Alert>
        <Alert kind="ok">{ok}</Alert>

        {tab === 'signin' ? <React.Fragment>
          <h2>Welcome back</h2>
          <p className="lede">Sign in to your portfolio.</p>
          <div className="field">
            <label>Email</label>
            <input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="you@example.com" autoComplete="email" />
          </div>
          <div className="field">
            <label>Password <button type="button" className="forgot linkbtn" onClick={forgot}>Forgot?</button></label>
            <input type="password" value={pw} onChange={e => setPw(e.target.value)} placeholder="••••••••" autoComplete="current-password"
              onKeyDown={e => e.key === 'Enter' && signin()} />
          </div>
          <button className="btn btn-primary" onClick={signin} disabled={busy}>{busy ? <Spinner /> : 'Sign in'}</button>
          <div className="auth-foot">New here? <button className="linkbtn" onClick={() => { setTab('signup'); reset(); }}>Create an account</button></div>
        </React.Fragment> : <React.Fragment>
          <h2>Start your free trial</h2>
          <p className="lede">14 days of everything. No card needed.</p>
          <div className="field">
            <label>Portfolio / company name</label>
            <input value={company} onChange={e => setCompany(e.target.value)} placeholder="e.g. Greenfield Family Portfolio" />
          </div>
          <div className="field">
            <label>Email</label>
            <input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="you@example.com" autoComplete="email" />
          </div>
          <div className="field">
            <label>Password</label>
            <input type="password" value={pw} onChange={e => setPw(e.target.value)} placeholder="at least 6 characters" autoComplete="new-password"
              onKeyDown={e => e.key === 'Enter' && signup()} />
            <div className="hint">You'll be the account admin — you can invite your team after.</div>
          </div>
          <button className="btn btn-primary" onClick={signup} disabled={busy}>{busy ? <Spinner /> : 'Create account & start trial'}</button>
          <div className="auth-foot">Already have an account? <button className="linkbtn" onClick={() => { setTab('signin'); reset(); }}>Sign in</button></div>
        </React.Fragment>}
      </div>
    </main>
  </div>;
}

// ════════════════════════════════════════════════════════════════════════
// HOME — account, plan/trial, properties, team + invites
// ════════════════════════════════════════════════════════════════════════
function Home({ sb, session, account, reload, toast }) {
  const [props, setProps] = useState(null);
  const [members, setMembers] = useState(null);
  const [certs, setCerts] = useState(null);
  const [extra, setExtra] = useState({});
  const [owners, setOwners] = useState([]);
  const [propOwners, setPropOwners] = useState([]);
  const [scope, setScope] = useState(() => { try { return localStorage.getItem('pm-scope') || 'all'; } catch { return 'all'; } });
  const [selected, setSelected] = useState(null);
  const [view, setView] = useState(() => { const q = new URLSearchParams(location.search); if (q.get('bank') || (q.get('code') && q.get('state'))) return 'bank'; try { return localStorage.getItem('pm-view') || 'dashboard'; } catch { return 'dashboard'; } });
  useEffect(() => { try { localStorage.setItem('pm-view', view); } catch (e) {} }, [view]);
  const [billing, setBilling] = useState(() => new URLSearchParams(location.search).get('billing') === 'success');
  const myEmail = session.user.email;
  const myRole = (members || []).find(m => m.user_id === session.user.id)?.role;
  const isAdmin = myRole === 'family_admin';

  const load = useCallback(async () => {
    const [p, m, c] = await Promise.all([
      sb.from('properties').select('*').order('created_at', { ascending: true }),
      sb.from('memberships').select('*').order('created_at', { ascending: true }),
      sb.from('certificates').select('property_id,type,expiry_date'),
    ]);
    setProps(p.data || []);
    setMembers(m.data || []);
    setCerts(c.data || []);
    // live dashboard signals — reads are open to members on every plan
    const [tk, br, pay, exp, ty, tn] = await Promise.all([
      sb.from('tickets').select('id,property_id,category,priority,status,raised_at,title'),
      sb.from('bank_rows').select('id,status,amount,date'),
      sb.from('payments').select('tenancy_id,amount,date'),
      sb.from('expenses').select('amount,date'),
      sb.from('tenancies').select('id,property_id,lead_tenant_id,status,rent_pcm,start_date'),
      sb.from('tenants').select('id,full_name'),
    ]);
    setExtra({ tickets: tk.data || [], bankRows: br.data || [], payments: pay.data || [], expenses: exp.data || [], tenancies: ty.data || [], tenants: tn.data || [] });
    const [ow, pow] = await Promise.all([
      sb.from('owners').select('id,name,color'),
      sb.from('property_owners').select('property_id,owner_id'),
    ]);
    setOwners(ow.data || []); setPropOwners(pow.data || []);
  }, [sb]);
  useEffect(() => { load(); }, [load]);

  const trial = daysLeft(account.trial_ends_at);
  const Detail = window.PMProperty;
  const PropsList = window.PMPropertiesList;
  const Compliance = window.PMCompliance;
  const Documents = window.PMDocuments;
  const Owners = window.PMOwners;
  const DashFull = window.PMDashboardFull;
  const BillingModal = window.PMBilling;
  const sum = summarise(certs);
  const openBilling = () => setBilling(true);
  const go = (v) => { setSelected(null); setView(v); };
  const pickScope = (v) => { setScope(v); try { localStorage.setItem('pm-scope', v); } catch {} };
  // owner scope — which property ids are visible
  const scopedIds = (() => {
    if (scope === 'all' || !props) return null;
    const ids = new Set();
    props.forEach(p => { if (p.primary_owner_id === scope) ids.add(p.id); });
    propOwners.forEach(r => { if (r.owner_id === scope) ids.add(r.property_id); });
    return ids;
  })();
  const scopedProps = scopedIds && props ? props.filter(p => scopedIds.has(p.id)) : props;
  const scopedCerts = scopedIds && certs ? certs.filter(c => scopedIds.has(c.property_id)) : certs;
  const scopedExtra = scopedIds ? { ...extra, tickets: (extra.tickets || []).filter(t => scopedIds.has(t.property_id)), tenancies: (extra.tenancies || []).filter(t => scopedIds.has(t.property_id)) } : extra;
  const activeKey = selected ? 'properties' : view;
  const nav = (v) => 'navitem' + (activeKey === v ? ' on' : '');

  const banners = <React.Fragment>
    {account.status === 'trialing' && trial != null && <div className="banner trial">
      <span>🎁</span>
      <span>{trial > 0 ? <React.Fragment><b>{trial} day{trial === 1 ? '' : 's'} left</b> in your free trial.</React.Fragment> : <React.Fragment>Your free trial has <b>ended</b>.</React.Fragment>} Choose a plan to keep everything running.</span>
      <span className="spacer" />
      {isAdmin && <button className="btn btn-primary btn-sm" onClick={openBilling} style={{ width: 'auto' }}>Choose a plan</button>}
    </div>}
    {account.status === 'past_due' && <div className="banner pastdue">
      <span>⚠</span><span>A payment failed — please update your card to avoid losing access.</span>
      <span className="spacer" />
      {isAdmin && <button className="btn btn-primary btn-sm" onClick={openBilling} style={{ width: 'auto' }}>Fix billing</button>}
    </div>}
  </React.Fragment>;

  return <React.Fragment>
    <div className="topbar">
      <div className="topbar-brand"><Wordmark /></div>
      <div className="acct">
        <span className="nm">{account.name}</span>
        <span className="pl">{(account.plan || 'starter').replace(/^\w/, c => c.toUpperCase())} plan</span>
      </div>
      <span className="plan-pill">{account.plan}</span>
      {account.status === 'trialing' && trial != null && <span className="trial-pill">{trial > 0 ? trial + ' days left' : 'trial ended'}</span>}
      <div className="spacer" />
      {owners.length > 0 && <select className="pm-input" style={{ padding: '7px 10px', fontSize: 13 }} value={scope} onChange={e => pickScope(e.target.value)} title="Owner scope — filters the dashboard and property list">
        <option value="all">All owners</option>
        {owners.map(o => <option key={o.id} value={o.id}>{o.name}</option>)}
      </select>}
      <span className="who">{myEmail}</span>
      <button className="btn btn-ghost btn-sm" onClick={() => sb.auth.signOut()}>Sign out</button>
    </div>

    <div className="app-layout">
      <nav className="sidebar">
        <div className="navgroup-label">Portfolio</div>
        <button className={nav('dashboard')} onClick={() => go('dashboard')}><span className="ic">◆</span> Dashboard</button>
        <button className={nav('properties')} onClick={() => go('properties')}><span className="ic">⌂</span> Properties</button>
        <button className={nav('compliance')} onClick={() => go('compliance')}><span className="ic">◑</span> Compliance</button>
        <div className="navgroup-label">People</div>
        <button className={nav('tenants')} onClick={() => go('tenants')}><span className="ic">☺</span> Tenants</button>
        <button className={nav('agreements')} onClick={() => go('agreements')}><span className="ic">✎</span> Agreements</button>
        <button className={nav('owners')} onClick={() => go('owners')}><span className="ic">◐</span> Owners</button>
        <div className="navgroup-label">Operations</div>
        <button className={nav('alerts')} onClick={() => go('alerts')}><span className="ic">!</span> Alerts</button>
        <button className={nav('maintenance')} onClick={() => go('maintenance')}><span className="ic">⚒</span> Maintenance</button>
        <button className={nav('inspections')} onClick={() => go('inspections')}><span className="ic">⌕</span> Inspections</button>
        <button className={nav('onsite')} onClick={() => go('onsite')}><span className="ic">◈</span> On-site</button>
        <div className="navgroup-label">Money</div>
        <button className={nav('ledger')} onClick={() => go('ledger')}><span className="ic">£</span> Ledger</button>
        <button className={nav('expenses')} onClick={() => go('expenses')}><span className="ic">−</span> Expenses</button>
        <button className={nav('bank')} onClick={() => go('bank')}><span className="ic">⇄</span> Bank</button>
        <button className={nav('tax')} onClick={() => go('tax')}><span className="ic">▦</span> Tax</button>
        <div className="navgroup-label">Tools</div>
        <button className={nav('communicate')} onClick={() => go('communicate')}><span className="ic">✉</span> Communicate</button>
        <button className={nav('exports')} onClick={() => go('exports')}><span className="ic">↓</span> Export centre</button>
        <button className={nav('import')} onClick={() => go('import')}><span className="ic">↑</span> Import data</button>
        <button className={nav('documents')} onClick={() => go('documents')}><span className="ic">▤</span> Documents</button>
        <button className={nav('activity')} onClick={() => go('activity')}><span className="ic">⟳</span> Activity log</button>
        <div className="nav-foot">
          <button className={nav('settings')} onClick={() => go('settings')}><span className="ic">⚙</span> Settings</button>
          {isAdmin && <button className="navitem" onClick={openBilling}><span className="ic">◷</span> Plans &amp; billing</button>}
        </div>
      </nav>

      <main className="content">
        {banners}
        {selected && Detail ? <Detail sb={sb} property={selected} account={account} onBack={() => { setSelected(null); load(); }} toast={toast} openBilling={openBilling} />
        : view === 'dashboard' ? (DashFull ? <DashFull account={account} props={scopedProps} certs={scopedCerts} extra={scopedExtra} go={go} onOpen={(p) => setSelected(p)} /> : <Dashboard account={account} props={scopedProps} certs={scopedCerts} sum={sum} onOpen={(p) => setSelected(p)} go={go} />)
        : view === 'properties' ? (PropsList ? <PropsList sb={sb} account={account} toast={toast} onOpen={setSelected} openBilling={openBilling} initialOwner={scope} /> : <React.Fragment>
            <div className="pagehead"><h1>Properties</h1><p>{props ? props.length : 0} in your portfolio.</p></div>
            <PropertiesCard sb={sb} props={props} reload={load} toast={toast} cap={account.property_cap} onOpen={setSelected} onUpgrade={openBilling} />
          </React.Fragment>)
        : view === 'compliance' ? (window.PMCompliance ? <Compliance sb={sb} onOpenProperty={setSelected} /> : null)
        : view === 'documents' ? <React.Fragment>
            <div className="pagehead"><h1>Documents</h1><p>Your private document vault — only this account can open these files.</p></div>
            {Documents && <Documents sb={sb} account={account} toast={toast} />}
          </React.Fragment>
        : view === 'alerts' ? (window.PMAlertsHub ? <window.PMAlertsHub sb={sb} account={account} go={go} /> : null)
        : view === 'communicate' ? (window.PMCommunicateHub ? <window.PMCommunicateHub sb={sb} account={account} toast={toast} isAdmin={isAdmin} /> : null)
        : view === 'onsite' ? (window.PMOnsite ? <window.PMOnsite sb={sb} account={account} toast={toast} openBilling={openBilling} go={go} /> : null)
        : view === 'exports' ? (window.PMExportCentre ? <window.PMExportCentre sb={sb} account={account} openBilling={openBilling} toast={toast} /> : null)
        : view === 'import' ? (window.PMImportHub ? <window.PMImportHub sb={sb} account={account} toast={toast} openBilling={openBilling} go={go} /> : null)
        : view === 'settings' ? (window.PMSettingsHub ? <window.PMSettingsHub sb={sb} account={account} isAdmin={isAdmin} toast={toast} go={go} myEmail={myEmail} myId={session.user.id} /> : null)
        : view === 'activity' ? (window.PMActivityLog ? <window.PMActivityLog sb={sb} account={account} /> : null)
        : view === 'ledger' ? (window.PMLedgerHub ? <window.PMLedgerHub sb={sb} account={account} toast={toast} openBilling={openBilling} /> : null)
        : view === 'maintenance' ? (window.PMMaintHub ? <window.PMMaintHub sb={sb} account={account} toast={toast} openBilling={openBilling} onOpenProperty={setSelected} /> : null)
        : view === 'inspections' ? (window.PMInspectHub ? <window.PMInspectHub sb={sb} account={account} toast={toast} openBilling={openBilling} onOpenProperty={setSelected} /> : null)
        : view === 'expenses' ? (window.PMExpensesHub ? <window.PMExpensesHub sb={sb} account={account} toast={toast} openBilling={openBilling} onOpenProperty={setSelected} /> : null)
        : view === 'bank' ? (window.PMBankHub ? <window.PMBankHub sb={sb} account={account} toast={toast} openBilling={openBilling} /> : null)
        : view === 'tax' ? (window.PMTaxHub ? <window.PMTaxHub sb={sb} account={account} toast={toast} openBilling={openBilling} membership={(members || []).find(m => m.user_id === session.user.id)} /> : null)
        : view === 'agreements' ? (window.PMAgreementsHub ? <window.PMAgreementsHub sb={sb} account={account} toast={toast} /> : null)
        : view === 'tenants' ? (window.PMTenantsHub ? <window.PMTenantsHub sb={sb} account={account} toast={toast} onOpenProperty={setSelected} /> : null)
        : view === 'owners' ? (Owners ? <Owners sb={sb} account={account} toast={toast} onUpgrade={openBilling} onOpenProperty={setSelected} /> : null)
        : view === 'team' ? <React.Fragment>
            <div className="pagehead"><h1>Team</h1><p>People with access to this account.</p></div>
            <TeamCard sb={sb} members={members} account={account} isAdmin={isAdmin} myId={session.user.id} reload={load} toast={toast} onUpgrade={openBilling} />
          </React.Fragment>
        : null}
      </main>
    </div>

    {billing && BillingModal && <BillingModal sb={sb} account={account} toast={toast} onClose={() => { setBilling(false); history.replaceState({}, '', location.pathname); reload(); }} />}
  </React.Fragment>;
}

// ════════════════════════════════════════════════════════════════════════
// DASHBOARD — Mission Control: portfolio roll-up + what needs attention
// ════════════════════════════════════════════════════════════════════════
function Dashboard({ account, props, certs, sum, onOpen, go }) {
  const CERTLBL = { gas_safety: 'Gas Safety', eicr: 'Electrical (EICR)', epc: 'EPC', deposit_protection: 'Deposit protection', right_to_rent: 'Right to Rent', hmo_licence: 'HMO licence', insurance: 'Insurance', hmo_inspection: 'HMO inspection' };
  const byId = props ? Object.fromEntries(props.map(p => [p.id, p])) : {};
  const fmt = (d) => new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
  const attention = [];
  (certs || []).forEach(c => {
    if (!c.expiry_date) return;
    const days = Math.ceil((new Date(c.expiry_date) - new Date()) / 86400000);
    if (days <= 60) attention.push({ ...c, days });
  });
  attention.sort((a, b) => a.days - b.days);

  return <React.Fragment>
    <div className="pagehead"><h1>{account.name}</h1><p>Your portfolio at a glance.</p></div>

    <div className="summary">
      <div className="sumcard ok"><div className="n">{sum ? sum.valid : 0}</div><div className="l">Valid certificates</div></div>
      <div className="sumcard warn"><div className="n">{sum ? sum.expiring : 0}</div><div className="l">Due within 60 days</div></div>
      <div className="sumcard bad"><div className="n">{sum ? sum.expired : 0}</div><div className="l">Expired</div></div>
      <div className="sumcard"><div className="n">{props ? props.length : 0}</div><div className="l">Properties</div></div>
    </div>

    <div className="card" style={{ marginTop: 18 }}>
      <div className="card-head"><div><h3>Needs attention</h3><div className="sub">Certificates expired or due within 60 days</div></div></div>
      {certs === null ? <div className="card-pad"><span className="spin dark" /></div>
        : attention.length === 0 ? <div className="empty" style={{ padding: '28px 20px' }}><div className="ico">✓</div><h3>All clear</h3><p>Nothing expiring soon across your portfolio. Renewals will appear here as they approach.</p></div>
        : attention.map((c, i) => { const prop = byId[c.property_id] || {}; const bad = c.days < 0; return <div className="row clickable" key={i} onClick={() => prop.id && onOpen(prop)}>
            <span className="avatar" style={{ background: bad ? 'var(--red-soft)' : 'var(--amber-soft)', color: bad ? '#8f3328' : '#8a5418' }}>{(CERTLBL[c.type] || '?')[0]}</span>
            <div className="main"><div className="t">{CERTLBL[c.type] || c.type}</div><div className="s">{prop.address || 'Property'} · expires {fmt(c.expiry_date)}</div></div>
            <span className={'badge ' + (bad ? 'bad' : 'warn')}>{bad ? 'Expired' : 'Due ' + c.days + 'd'}</span>
          </div>; })}
    </div>

    {props && props.length === 0 && <div className="card" style={{ marginTop: 18 }}>
      <div className="empty"><div className="ico">🏠</div><h3>Add your first property</h3><p>Once it's in, PropMystro tracks its certificates, deadlines and finances.</p><button className="btn btn-primary btn-sm" onClick={() => go('properties')}>Go to Properties</button></div>
    </div>}
  </React.Fragment>;
}

// portfolio-wide certificate roll-up for the home summary strip
function summarise(certs) {
  if (!certs) return null;
  let valid = 0, expiring = 0, expired = 0;
  for (const c of certs) {
    if (!c.expiry_date) continue;
    const days = Math.ceil((new Date(c.expiry_date) - new Date()) / 86400000);
    if (days < 0) expired++; else if (days <= 60) expiring++; else valid++;
  }
  return { valid, expiring, expired };
}

function PropertiesCard({ sb, props, reload, toast, cap, onOpen, onUpgrade }) {
  const [adding, setAdding] = useState(false);
  const [f, setF] = useState({ address: '', postcode: '', type: 'flat', beds: '', rent: '' });
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');

  const submit = async () => {
    setErr('');
    if (!f.address.trim() || !f.postcode.trim()) return setErr('Address and postcode are required.');
    setBusy(true);
    const { error } = await sb.from('properties').insert({
      address: f.address.trim(), postcode: f.postcode.trim().toUpperCase(), type: f.type,
      beds: f.beds ? Number(f.beds) : null, rent: f.rent ? Number(f.rent) : null,
    });
    setBusy(false);
    if (error) { setErr(error.message === 'property_cap_reached' || /property_cap/.test(error.message) ? 'cap' : error.message); return; }
    setF({ address: '', postcode: '', type: 'flat', beds: '', rent: '' });
    setAdding(false); toast('Property added'); reload();
  };

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

  return <div className="card">
    <div className="card-head">
      <div><h3>Properties</h3><div className="sub">{props.length} of {cap >= 100000 ? '∞' : cap} on your plan</div></div>
      {props.length > 0 && !adding && <button className="btn btn-ghost btn-sm" onClick={() => setAdding(true)}>+ Add property</button>}
    </div>

    {props.length === 0 && !adding && <div className="empty">
      <div className="ico">🏠</div>
      <h3>Add your first property</h3>
      <p>Once it's in, PropMystro starts tracking its certificates, deadlines and finances for you.</p>
      <button className="btn btn-primary btn-sm" onClick={() => setAdding(true)}>+ Add a property</button>
    </div>}

    {adding && <div className="card-pad" style={{ borderBottom: props.length ? '1px solid var(--line-soft)' : 'none' }}>
      {err && <div className="alert alert-err"><span className="ic">⚠</span><div>{err === 'cap' ? <React.Fragment>You've reached your plan's property limit. {onUpgrade && <button className="linkbtn" onClick={onUpgrade}>Upgrade to add more →</button>}</React.Fragment> : err}</div></div>}
      <div className="grid2" style={{ marginBottom: 12 }}>
        <div className="field" style={{ margin: 0 }}><label>Address</label><input value={f.address} onChange={e => setF({ ...f, address: e.target.value })} placeholder="14 Eldon Road" /></div>
        <div className="field" style={{ margin: 0 }}><label>Postcode</label><input value={f.postcode} onChange={e => setF({ ...f, postcode: e.target.value })} placeholder="HA1 5HU" /></div>
      </div>
      <div className="inline-form">
        <div className="field"><label>Type</label><select value={f.type} onChange={e => setF({ ...f, type: e.target.value })}>{PROP_TYPES.map(t => <option key={t.v} value={t.v}>{t.l}</option>)}</select></div>
        <div className="field"><label>Bedrooms</label><input style={{ width: 90 }} value={f.beds} onChange={e => setF({ ...f, beds: e.target.value.replace(/\D/g, '') })} placeholder="3" /></div>
        <div className="field grow"><label>Rent (£ pcm)</label><input value={f.rent} onChange={e => setF({ ...f, rent: e.target.value.replace(/[^\d.]/g, '') })} placeholder="2450" /></div>
        <button className="btn btn-primary btn-sm" onClick={submit} disabled={busy}>{busy ? <Spinner /> : 'Save property'}</button>
        <button className="btn btn-ghost btn-sm" onClick={() => { setAdding(false); setErr(''); }}>Cancel</button>
      </div>
    </div>}

    {props.map(p => <div className="row clickable" key={p.id} onClick={() => onOpen && onOpen(p)}>
      <span className="avatar">{(p.type || 'F')[0].toUpperCase()}</span>
      <div className="main">
        <div className="t">{p.address}</div>
        <div className="s">{p.postcode} · {(p.type || '').toUpperCase()}{p.beds ? ' · ' + p.beds + ' bed' : ''}{p.rent ? ' · £' + Number(p.rent).toLocaleString() + ' pcm' : ''}</div>
      </div>
      <span className="chev">›</span>
    </div>)}
  </div>;
}

function TeamCard({ sb, members, account, isAdmin, myId, reload, toast, onUpgrade }) {
  const [email, setEmail] = useState('');
  const [role, setRole] = useState('accountant');
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState('');
  const [invite, setInvite] = useState(null);

  const send = async () => {
    setErr(''); setInvite(null);
    if (!email.trim()) return setErr('Enter an email to invite.');
    setBusy(true);
    const { data, error } = await sb.rpc('invite_member', { acct: account.id, member_email: email.trim(), member_role: role });
    setBusy(false);
    if (error) { setErr(/seat_limit/.test(error.message) ? 'seat' : error.message); return; }
    const link = location.origin + location.pathname + '?invite=' + data;
    setInvite(link); setEmail(''); toast('Invitation created');
  };

  if (members === null) return null;

  return <div className="card">
    <div className="card-head">
      <div><h3>Your team</h3><div className="sub">{members.length} of {account.seat_cap >= 100000 ? '∞' : account.seat_cap} seat{account.seat_cap === 1 ? '' : 's'}</div></div>
    </div>

    {members.map(m => <div className="row" key={m.id}>
      <span className="avatar">{initials(m.user_id === myId ? 'You' : (m.owner_key || 'M'))}</span>
      <div className="main">
        <div className="t">{m.user_id === myId ? 'You' : (m.owner_key ? m.owner_key : 'Team member')}{m.status !== 'active' ? ' · ' + m.status : ''}</div>
        <div className="s">{m.user_id === myId ? 'This is you' : 'Member'}</div>
      </div>
      <span className="role-tag">{ROLE_LABEL[m.role] || m.role}</span>
    </div>)}

    {isAdmin ? <div className="card-pad" style={{ borderTop: '1px solid var(--line-soft)' }}>
      {err && <div className="alert alert-err"><span className="ic">⚠</span><div>{err === 'seat' ? <React.Fragment>You've reached your plan's team-seat limit. {onUpgrade && <button className="linkbtn" onClick={onUpgrade}>Upgrade for more seats →</button>}</React.Fragment> : err}</div></div>}
      {invite && <Alert kind="ok"><div>Invitation created. Share this link with them (in the real product this is emailed automatically):<br /><code style={{ fontSize: 12, wordBreak: 'break-all' }}>{invite}</code></div></Alert>}
      <div className="inline-form">
        <div className="field grow"><label>Invite a teammate</label><input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="accountant@firm.com" /></div>
        <div className="field"><label>Role</label><select value={role} onChange={e => setRole(e.target.value)}>{ROLES.map(r => <option key={r.v} value={r.v}>{r.l}</option>)}</select></div>
        <button className="btn btn-primary btn-sm" onClick={send} disabled={busy}>{busy ? <Spinner /> : 'Send invite'}</button>
      </div>
    </div> : <div className="card-pad" style={{ borderTop: '1px solid var(--line-soft)', color: 'var(--ink-faint)', fontSize: 13 }}>Only an account admin can invite teammates.</div>}
  </div>;
}

// ════════════════════════════════════════════════════════════════════════
// INVITE ACCEPT — if the URL carries ?invite=token and you're signed in
// ════════════════════════════════════════════════════════════════════════
function AcceptInvite({ sb, token, session, onDone }) {
  const [state, setState] = useState('idle'); // idle | busy | done | err
  const [msg, setMsg] = useState('');
  const accept = async () => {
    setState('busy');
    const { error } = await sb.rpc('accept_invite', { tok: token });
    if (error) { setState('err'); setMsg(error.message); }
    else { setState('done'); setMsg('You\'ve joined the account.'); setTimeout(onDone, 1200); }
  };
  return <div className="connect"><div className="connect-card">
    <Wordmark />
    <h2 style={{ marginTop: 18 }}>You've been invited</h2>
    <p className="lede">Signed in as <b>{session.user.email}</b>. Accept to join this PropMystro account.</p>
    {state === 'err' && <Alert kind="err">{msg.replace('different email', 'a different email — sign in with the address the invite was sent to')}</Alert>}
    {state === 'done' && <Alert kind="ok">{msg}</Alert>}
    <div style={{ display: 'flex', gap: 8 }}>
      <button className="btn btn-primary" onClick={accept} disabled={state === 'busy' || state === 'done'}>{state === 'busy' ? <Spinner /> : 'Accept invitation'}</button>
      <button className="btn btn-ghost" onClick={onDone}>Not now</button>
    </div>
  </div></div>;
}

// ════════════════════════════════════════════════════════════════════════
// APP ROOT — wires connection + session + account loading
// ════════════════════════════════════════════════════════════════════════
function App() {
  const [connected, setConnected] = useState(!!(cfgUrl() && cfgAnon()));
  const [session, setSession] = useState(undefined);   // undefined = loading
  const [account, setAccount] = useState(null);
  const [acctLoading, setAcctLoading] = useState(false);
  const [toastMsg, setToastMsg] = useState('');
  const [inviteTok] = useState(() => new URLSearchParams(location.search).get('invite'));
  const [showInvite, setShowInvite] = useState(!!inviteTok);
  const sbRef = useRef(null);

  const toast = useCallback((m) => { setToastMsg(m); setTimeout(() => setToastMsg(''), 2600); }, []);

  // build (or rebuild) the client when connected
  useEffect(() => {
    if (!connected) return;
    const url = cfgUrl(), anon = cfgAnon();
    if (!window.supabase) return;
    sbRef.current = window.supabase.createClient(url, anon, { auth: { persistSession: true, storageKey: 'pm-session', autoRefreshToken: true } });
    sbRef.current.auth.getSession().then(({ data }) => setSession(data.session));
    const { data: sub } = sbRef.current.auth.onAuthStateChange((_e, s) => {
      // Only react when the signed-in USER actually changes. Background token
      // refreshes (e.g. on tab refocus) fire this with a new object for the
      // same user — reacting to those remounts Home and loses the open view.
      setSession(prev => {
        const prevId = prev && prev.user && prev.user.id;
        const nextId = s && s.user && s.user.id;
        return prevId === nextId ? prev : s;
      });
      if (!s) setAccount(null);
    });
    return () => sub.subscription.unsubscribe();
  }, [connected]);

  // load the account once signed in
  const loadAccount = useCallback(async () => {
    if (!sbRef.current || !session) return;
    setAcctLoading(true);
    const { data } = await sbRef.current.from('accounts').select('*').limit(1);
    setAccount((data && data[0]) || null);
    setAcctLoading(false);
  }, [session]);
  useEffect(() => { if (session) loadAccount(); }, [session, loadAccount]);

  if (!connected) return <ConnectGate onConnect={() => setConnected(true)} />;
  if (session === undefined) return <div className="center-load"><Spinner dark /></div>;
  if (!session) return <AuthScreen sb={sbRef.current} />;
  if (showInvite && inviteTok) return <AcceptInvite sb={sbRef.current} token={inviteTok} session={session}
    onDone={() => { history.replaceState({}, '', location.pathname); setShowInvite(false); loadAccount(); }} />;
  if (acctLoading || account === null) return <div className="center-load"><Spinner dark /></div>;

  return <React.Fragment>
    <Home sb={sbRef.current} session={session} account={account} reload={loadAccount} toast={toast} />
    {toastMsg && <div className="toast">{toastMsg}</div>}
  </React.Fragment>;
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
