// ════════════════════════════════════════════════════════════════════════
// PropMystro · G3 · pm-d-comms.jsx  (mirror of pm-communicate.jsx)
// Communicate — issue letters and notices to tenants from a merge-field
// template library: pick template + property → tokens resolve from the live
// tenancy → edit → review (with unresolved-placeholder guard) → send via
// your mail app + an append-only audit row in activity_log. Custom templates
// live in account_settings.data.letterTemplates (admin-edit). The Documents
// tab pre-fills a cover letter for any filed certificate.
// → window.PMCommunicateHub
// ════════════════════════════════════════════════════════════════════════
(function () {
  const { useState, useEffect, useCallback } = React;

  const TOKENS = [
    { key: 'tenant_name', label: 'Tenant name' },
    { key: 'property_address', label: 'Property address' },
    { key: 'postcode', label: 'Postcode' },
    { key: 'owner_name', label: 'Owner name' },
    { key: 'landlord_name', label: 'Landlord name' },
    { key: 'rent', label: 'Monthly rent' },
    { key: 'tenancy_start', label: 'Tenancy start' },
    { key: 'deposit_scheme', label: 'Deposit scheme' },
    { key: 'date', label: "Today's date" },
  ];

  const SEED_TEMPLATES = [
    { id: 'lt1', name: 'Rent increase notice (informal)', category: 'Rent',
      subject: 'Notice of proposed rent increase — {{property_address}}',
      body: 'Dear {{tenant_name}},\n\nI am writing regarding your tenancy at {{property_address}}. Your current rent is {{rent}} per calendar month.\n\nFrom [DATE], the rent will increase to £[AMOUNT] per calendar month. This reflects [reason].\n\nIf you have any questions, please get in touch.\n\nKind regards,\n{{landlord_name}}' },
    { id: 'lt2', name: 'General notice to tenant', category: 'General',
      subject: 'An update about your tenancy — {{property_address}}',
      body: 'Dear {{tenant_name}},\n\nI am writing to let you know [details].\n\nThis relates to your tenancy at {{property_address}}, which began on {{tenancy_start}}.\n\nPlease contact me if you have any questions.\n\nKind regards,\n{{landlord_name}}' },
    { id: 'lt3', name: 'Access for inspection / works', category: 'Access',
      subject: 'Arranging access — {{property_address}}',
      body: 'Dear {{tenant_name}},\n\nWe would like to arrange access to {{property_address}} on [DATE] at [TIME] for [purpose] (e.g. routine inspection / gas safety check / repairs).\n\nWe are required to give at least 24 hours\u2019 written notice and will always seek a convenient time. Please let me know if this works.\n\nKind regards,\n{{landlord_name}}' },
    { id: 'lt4', name: 'Arrears reminder', category: 'Arrears',
      subject: 'Rent payment reminder — {{property_address}}',
      body: 'Dear {{tenant_name}},\n\nOur records show rent on your tenancy at {{property_address}} is currently in arrears of £[AMOUNT].\n\nPlease arrange payment at your earliest convenience, or contact me to discuss a plan. Your rent is {{rent}} per calendar month.\n\nKind regards,\n{{landlord_name}}' },
    { id: 'lt5', name: 'End-of-tenancy / move-out', category: 'General',
      subject: 'Your upcoming move-out — {{property_address}}',
      body: 'Dear {{tenant_name}},\n\nAhead of the end of your tenancy at {{property_address}}, here is what to expect regarding the check-out, return of your deposit (held with {{deposit_scheme}}), and final meter readings.\n\n[details]\n\nKind regards,\n{{landlord_name}}' },
    { id: 'lt6', name: 'Document cover letter', category: 'Documents',
      subject: 'A document for your records — {{property_address}}',
      body: 'Dear {{tenant_name}},\n\nPlease find attached the [DOCUMENT] for {{property_address}}, for your records.\n\nIf you have any questions about it, do let me know.\n\nKind regards,\n{{landlord_name}}' },
  ];

  const CERT_LABELS = { gas_safety: 'Gas Safety certificate', eicr: 'Electrical (EICR) report', epc: 'EPC certificate', deposit_protection: 'Deposit protection certificate', right_to_rent: 'Right to Rent check', hmo_licence: 'HMO licence', insurance: 'Insurance schedule', hmo_inspection: 'HMO inspection report', info_sheet: 'Tenancy information sheet', passport_id: 'ID document' };

  const merge = (text, tokens) => String(text || '').replace(/\{\{(\w+)\}\}/g, (m, k) => (tokens && tokens[k] != null && tokens[k] !== '') ? tokens[k] : m);
  const gbp = (n) => '£' + (Number(n) || 0).toLocaleString();
  const fmt = (d) => d ? new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : '—';
  const fmtT = (d) => d ? new Date(d).toLocaleString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : '—';
  function Spin() { return <span className="spin dark" />; }

  // ── hub ──────────────────────────────────────────────────────────────
  function CommunicateHub({ sb, account, toast, isAdmin }) {
    const [tab, setTab] = useState('compose');
    const [d, setD] = useState(null);
    const [custom, setCustom] = useState([]);       // custom templates from settings
    const [sent, setSent] = useState(null);
    const [prefill, setPrefill] = useState(null);   // from Documents tab

    const load = useCallback(async () => {
      const [p, o, ty, tn, s, c] = await Promise.all([
        sb.from('properties').select('id,address,postcode,primary_owner_id,rent').order('address', { ascending: true }),
        sb.from('owners').select('id,name,email'),
        sb.from('tenancies').select('id,property_id,lead_tenant_id,status,rent_pcm,start_date,deposit_scheme'),
        sb.from('tenants').select('id,full_name,email,phone'),
        sb.from('account_settings').select('data').eq('account_id', account.id).maybeSingle(),
        sb.from('certificates').select('id,property_id,type,cert_number,inspected_date,expiry_date,file_name').order('created_at', { ascending: false }).limit(80),
      ]);
      setD({ props: p.data || [], owners: o.data || [], tenancies: ty.data || [], tenants: tn.data || [], certs: c.data || [] });
      setCustom(((s.data && s.data.data) || {}).letterTemplates || []);
    }, [sb, account.id]);
    useEffect(() => { load(); }, [load]);

    const loadSent = useCallback(async () => {
      const { data } = await sb.from('activity_log').select('at,who,action,details').eq('entity_type', 'letter').order('at', { ascending: false }).limit(100);
      setSent(data || []);
    }, [sb]);
    useEffect(() => { if (tab === 'sent') loadSent(); }, [tab, loadSent]);

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

    const templates = SEED_TEMPLATES.concat(custom);

    const saveTemplates = async (next) => {
      const { data: cur } = await sb.from('account_settings').select('data').eq('account_id', account.id).maybeSingle();
      const blob = { ...((cur && cur.data) || {}), letterTemplates: next };
      const { error } = await sb.from('account_settings').upsert({ account_id: account.id, data: blob });
      if (error) { toast(isAdmin ? error.message : 'Only an account admin can edit templates'); return false; }
      setCustom(next); return true;
    };

    return <React.Fragment>
      <CommsStyles />
      <div className="pagehead"><h1>Communicate</h1><p>Reach tenants the way they actually reply — email, SMS or WhatsApp — from one merge-field template library. Tenant, property and owner details merge in automatically; review, then send from your own app. Every message is logged.</p></div>

      <div className="pm-seg" style={{ marginBottom: 16 }}>
        <button className={'pm-seg-btn' + (tab === 'compose' ? ' on' : '')} onClick={() => setTab('compose')}>Compose</button>
        <button className={'pm-seg-btn' + (tab === 'templates' ? ' on' : '')} onClick={() => setTab('templates')}>Templates</button>
        <button className={'pm-seg-btn' + (tab === 'documents' ? ' on' : '')} onClick={() => setTab('documents')}>Filed documents</button>
        <button className={'pm-seg-btn' + (tab === 'sent' ? ' on' : '')} onClick={() => setTab('sent')}>Sent log</button>
      </div>

      {tab === 'compose' && <Compose sb={sb} d={d} templates={templates} toast={toast} prefill={prefill} clearPrefill={() => setPrefill(null)} />}
      {tab === 'templates' && <Templates custom={custom} saveTemplates={saveTemplates} isAdmin={isAdmin} />}
      {tab === 'documents' && <FiledDocs d={d} onCompose={(pf) => { setPrefill(pf); setTab('compose'); }} />}
      {tab === 'sent' && <SentLog sent={sent} />}
    </React.Fragment>;
  }

  // resolve merge context for a property
  function contextFor(d, propertyId) {
    const property = d.props.find(p => p.id === propertyId);
    if (!property) return null;
    const tys = d.tenancies.filter(t => t.property_id === propertyId);
    const tenancy = tys.find(t => t.status === 'active') || tys.find(t => t.status === 'onboarding') || tys[0] || null;
    const lead = tenancy ? d.tenants.find(t => t.id === tenancy.lead_tenant_id) : null;
    const owner = property.primary_owner_id ? d.owners.find(o => o.id === property.primary_owner_id) : null;
    return {
      property, tenancy, lead, owner,
      tokens: {
        tenant_name: lead ? lead.full_name : '[tenant]',
        property_address: property.address,
        postcode: property.postcode || '',
        owner_name: owner ? owner.name : '[owner]',
        landlord_name: owner ? owner.name : '[landlord]',
        rent: gbp(Number(tenancy && tenancy.rent_pcm) || property.rent || 0),
        tenancy_start: tenancy && tenancy.start_date ? fmt(tenancy.start_date) : '[start date]',
        deposit_scheme: (tenancy && tenancy.deposit_scheme) || '[scheme]',
        date: fmt(new Date().toISOString().slice(0, 10)),
      },
    };
  }

  // channels — email (mailto), SMS (sms: deep link), WhatsApp (wa.me deep link).
  // All open the user's own app pre-filled; no third-party backend needed yet.
  const CHANNELS = [
    { key: 'email', label: 'Email', icon: '✉', verb: 'email app', need: 'email' },
    { key: 'sms', label: 'SMS', icon: '✉', verb: 'messages app', need: 'phone' },
    { key: 'whatsapp', label: 'WhatsApp', icon: '●', verb: 'WhatsApp', need: 'phone' },
  ];
  // UK-friendly E.164-ish: strip spaces/punctuation; leading 0 → 44; keep + as 00-less.
  function intlDigits(raw) {
    let s = String(raw || '').replace(/[^\d+]/g, '');
    if (s.startsWith('+')) return s.slice(1);
    if (s.startsWith('00')) return s.slice(2);
    if (s.startsWith('0')) return '44' + s.slice(1);
    return s;
  }

  // ── COMPOSE — template → property → merged draft → review → send ─────
  function Compose({ sb, d, templates, toast, prefill, clearPrefill }) {
    const [templateId, setTemplateId] = useState((prefill && prefill.templateId) || (templates[0] ? templates[0].id : ''));
    const [propertyId, setPropertyId] = useState((prefill && prefill.propertyId) || (d.props[0] ? d.props[0].id : ''));
    const [channel, setChannel] = useState('email');
    const [subject, setSubject] = useState('');
    const [bodyText, setBodyText] = useState('');
    const [to, setTo] = useState('');        // email address
    const [toPhone, setToPhone] = useState('');
    const [phase, setPhase] = useState('edit'); // edit | review | sent
    const tmpl = templates.find(t => t.id === templateId);
    const ctx = contextFor(d, propertyId);
    const ch = CHANNELS.find(c => c.key === channel) || CHANNELS[0];
    const isMsg = channel !== 'email';

    useEffect(() => {
      if (!tmpl || !ctx) return;
      const extra = prefill && prefill.fill ? prefill.fill : null;
      let sub = merge(tmpl.subject, ctx.tokens);
      let bod = merge(tmpl.body, ctx.tokens);
      if (extra) { Object.entries(extra).forEach(([k, v]) => { sub = sub.split(k).join(v); bod = bod.split(k).join(v); }); }
      setSubject(sub); setBodyText(bod);
      setTo(ctx.lead && ctx.lead.email ? ctx.lead.email : '');
      setToPhone(ctx.lead && ctx.lead.phone ? ctx.lead.phone : '');
      if (prefill) clearPrefill();
    }, [templateId, propertyId]);   // eslint-disable-line

    const placeholders = (bodyText.match(/\[[A-Za-z][^\]\n]{0,40}\]/g) || []).concat(subject.match(/\[[A-Za-z][^\]\n]{0,40}\]/g) || []);
    const unresolved = (bodyText.match(/\{\{\w+\}\}/g) || []).concat(subject.match(/\{\{\w+\}\}/g) || []);
    const recipient = isMsg ? toPhone : to;
    // SMS/WhatsApp carry the subject as a bold first line so context isn't lost.
    const msgText = isMsg && subject.trim() ? subject.trim() + '\n\n' + bodyText : bodyText;

    const send = async () => {
      let link, verb;
      if (channel === 'email') {
        link = 'mailto:' + encodeURIComponent(to) + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(bodyText);
        window.open(link, '_self'); verb = 'email app';
      } else if (channel === 'sms') {
        link = 'sms:' + intlDigits(toPhone) + '?&body=' + encodeURIComponent(msgText);
        window.open(link, '_self'); verb = 'messages app';
      } else {
        link = 'https://wa.me/' + intlDigits(toPhone) + '?text=' + encodeURIComponent(msgText);
        window.open(link, '_blank'); verb = 'WhatsApp';
      }
      await sb.from('activity_log').insert({
        who: 'Landlord',
        action: ch.label + ' sent — "' + (subject || 'message') + '" to ' + (ctx && ctx.lead ? ctx.lead.full_name : recipient),
        entity_type: 'letter', entity_id: propertyId,
        details: { channel, to: recipient, subject, body: msgText, template: tmpl ? tmpl.name : ch.label, property: ctx ? ctx.property.address : '', tenant: ctx && ctx.lead ? ctx.lead.full_name : '' },
      });
      setPhase('sent');
    };

    if (phase === 'sent') {
      return <div className="card card-pad" style={{ textAlign: 'center', padding: '36px 20px' }}>
        <div className="empty" style={{ padding: 0 }}>
          <div className="ico">✓</div>
          <h3>Handed to your {ch.verb}</h3>
          <p>“{subject || 'Message'}” for {recipient || 'the tenant'}. {channel === 'whatsapp' ? 'WhatsApp opened in a new tab — ' : channel === 'sms' ? 'Your messages app opened — ' : ''}hit send there. A copy is already in the Sent log here.</p>
          <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} onClick={() => setPhase('edit')}>Write another</button>
        </div>
      </div>;
    }

    return <div className="cm-grid">
      <div className="card cm-side">
        <div className="card-pad">
          <div className="field">
            <label>Channel</label>
            <div className="cm-channels">
              {CHANNELS.map(c => <button key={c.key} className={'cm-channel' + (channel === c.key ? ' on cm-channel-' + c.key : '')} onClick={() => setChannel(c.key)}>{c.label}</button>)}
            </div>
          </div>
          <div className="field">
            <label>Template</label>
            <select className="pm-input" style={{ width: '100%' }} value={templateId} onChange={e => setTemplateId(e.target.value)}>
              {templates.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
          </div>
          <div className="field">
            <label>Property / tenant</label>
            <select className="pm-input" style={{ width: '100%' }} value={propertyId} onChange={e => setPropertyId(e.target.value)}>
              {d.props.length === 0 && <option value="">No properties yet</option>}
              {d.props.map(p => <option key={p.id} value={p.id}>{p.address}</option>)}
            </select>
          </div>
          {ctx && <div className="cm-ctx">
            <div><span className="pmd-mono cm-ctx-l">TO</span> <strong>{ctx.lead ? ctx.lead.full_name : 'No current tenant'}</strong>
              {ctx.lead && ch.need === 'email' && !ctx.lead.email && <span className="badge warn" style={{ marginLeft: 8 }}>no email</span>}
              {ctx.lead && ch.need === 'phone' && !ctx.lead.phone && <span className="badge warn" style={{ marginLeft: 8 }}>no mobile</span>}</div>
            <div style={{ marginTop: 6 }}><span className="pmd-mono cm-ctx-l">VIA</span> {ch.label} · opens your {ch.verb}</div>
          </div>}
        </div>
      </div>

      <div className="card">
        <div className="card-head">
          <div><h3>{phase === 'review' ? 'Review before sending' : 'Draft'}</h3>
            <div className="sub">{phase === 'review' ? 'Check the details — this is what they receive.' : (isMsg ? 'A short message — tokens merged from the live tenancy.' : 'Tokens merged from the live tenancy — edit freely.')}</div></div>
        </div>
        <div className="card-pad">
          {phase === 'edit' ? <React.Fragment>
            {isMsg
              ? <div className="field"><label>Mobile number</label>
                  <input type="tel" value={toPhone} placeholder="07700 900000" onChange={e => setToPhone(e.target.value)} />
                  {toPhone && <span className="pmd-mono cm-hint">sends to +{intlDigits(toPhone)}</span>}</div>
              : <div className="field"><label>Email to</label>
                  <input type="email" value={to} placeholder="tenant@email.com" onChange={e => setTo(e.target.value)} /></div>}
            <div className="field"><label>{isMsg ? 'Headline (first line of the message)' : 'Subject'}</label>
              <input value={subject} onChange={e => setSubject(e.target.value)} /></div>
            <div className="field"><label>{isMsg ? 'Message' : 'Body'}</label>
              <textarea className="pm-textarea" rows={isMsg ? 7 : 13} value={bodyText} onChange={e => setBodyText(e.target.value)} style={{ lineHeight: 1.6 }}></textarea>
              {isMsg && <span className="pmd-mono cm-hint">{msgText.length} characters{channel === 'sms' ? ' · ~' + Math.max(1, Math.ceil(msgText.length / 160)) + ' SMS' : ''}</span>}</div>
            <div className="cm-actions">
              <span className="pmd-mono cm-hint">{placeholders.length > 0 ? placeholders.length + ' [bracketed] placeholder' + (placeholders.length === 1 ? '' : 's') + ' to fill' : 'ready to review'}</span>
              <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} disabled={!subject.trim() || !bodyText.trim()} onClick={() => setPhase('review')}>Review →</button>
            </div>
          </React.Fragment> : <React.Fragment>
            {placeholders.length > 0 && <div className="alert alert-info"><span className="ic">ℹ</span><div>Still contains {placeholders.slice(0, 4).join(', ')}{placeholders.length > 4 ? '…' : ''} — go back and fill these before sending.</div></div>}
            {unresolved.length > 0 && <div className="alert alert-err"><span className="ic">⚠</span><div>Unmerged tokens: {unresolved.join(', ')} — the tenancy is missing this detail.</div></div>}
            <div className="cm-preview">
              <div className="cm-preview-meta">
                <div><span className="pmd-mono cm-ctx-l">{isMsg ? 'MOBILE' : 'TO'}</span> {recipient ? (isMsg ? '+' + intlDigits(recipient) : recipient) : (isMsg ? '[no mobile]' : '[no email]')} <span className={'cm-via-pill cm-channel-' + channel + ' on'}>{ch.label}</span></div>
                {!isMsg && <div><span className="pmd-mono cm-ctx-l">SUBJECT</span> <strong>{subject}</strong></div>}
              </div>
              <div className="cm-preview-body">{isMsg ? msgText : bodyText}</div>
            </div>
            {!recipient && <div className="alert alert-err" style={{ marginTop: 12 }}><span className="ic">⚠</span><div>Add a tenant {isMsg ? 'mobile number' : 'email'} before sending.</div></div>}
            <div className="cm-actions">
              <button className="btn btn-ghost btn-sm" onClick={() => setPhase('edit')}>← Edit</button>
              <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} disabled={!recipient} onClick={send}>Approve &amp; send via {ch.label}</button>
            </div>
          </React.Fragment>}
        </div>
      </div>
    </div>;
  }

  // ── TEMPLATES — seed library + admin-editable custom templates ───────
  function Templates({ custom, saveTemplates, isAdmin }) {
    const [editing, setEditing] = useState(null);
    const blank = { id: '', name: '', category: 'General', subject: '', body: '' };
    const [draft, setDraft] = useState(blank);
    const startEdit = (t) => { setEditing(t.id); setDraft({ ...t }); };
    const startNew = () => { setEditing('new'); setDraft({ ...blank, id: 'lt' + Date.now() }); };
    const save = async () => {
      const next = custom.some(t => t.id === draft.id) ? custom.map(t => t.id === draft.id ? draft : t) : custom.concat([draft]);
      if (await saveTemplates(next)) setEditing(null);
    };
    const remove = async (id) => { await saveTemplates(custom.filter(t => t.id !== id)); };
    const insertToken = (k) => setDraft(dr => ({ ...dr, body: dr.body + '{{' + k + '}}' }));

    if (editing) {
      return <div className="card">
        <div className="card-head"><div><h3>{editing === 'new' ? 'New template' : 'Edit template'}</h3><div className="sub">Tokens in {'{{double braces}}'} merge automatically; [BRACKETS] are fill-in-by-hand placeholders.</div></div></div>
        <div className="card-pad">
          <div className="grid2" style={{ marginBottom: 12 }}>
            <div className="field" style={{ margin: 0 }}><label>Name</label><input value={draft.name} onChange={e => setDraft({ ...draft, name: e.target.value })} /></div>
            <div className="field" style={{ margin: 0 }}><label>Category</label><input value={draft.category} onChange={e => setDraft({ ...draft, category: e.target.value })} /></div>
          </div>
          <div className="field"><label>Subject</label><input value={draft.subject} onChange={e => setDraft({ ...draft, subject: e.target.value })} /></div>
          <div className="field"><label>Body</label><textarea className="pm-textarea" rows={11} value={draft.body} onChange={e => setDraft({ ...draft, body: e.target.value })}></textarea></div>
          <div className="cm-tokens">
            <span className="pmd-mono cm-ctx-l">INSERT:</span>
            {TOKENS.map(t => <button key={t.key} className="cm-token" onClick={() => insertToken(t.key)}>{t.label}</button>)}
          </div>
          <div className="cm-actions">
            <button className="btn btn-ghost btn-sm" onClick={() => setEditing(null)}>Cancel</button>
            <button className="btn btn-primary btn-sm" style={{ width: 'auto' }} disabled={!draft.name.trim() || !draft.body.trim()} onClick={save}>Save template</button>
          </div>
        </div>
      </div>;
    }

    return <React.Fragment>
      <div className="pm-toolbar" style={{ justifyContent: 'flex-end' }}>
        <span className="pmd-mono cm-hint" style={{ marginRight: 'auto' }}>{isAdmin ? 'Built-in templates can\u2019t be deleted — add your own alongside them.' : 'View only — an admin can add custom templates.'}</span>
        {isAdmin && <button className="btn btn-primary btn-sm" onClick={startNew}>+ New template</button>}
      </div>
      <div className="card">
        {SEED_TEMPLATES.map(t => <div className="row" key={t.id}>
          <span className="avatar">✉</span>
          <div className="main"><div className="t">{t.name} <span className="role-tag" style={{ marginLeft: 6 }}>{t.category}</span></div><div className="s">{t.subject}</div></div>
          <span className="pmd-mono cm-hint">built-in</span>
        </div>)}
        {custom.map(t => <div className="row" key={t.id}>
          <span className="avatar" style={{ background: 'var(--amber-soft)', color: '#8a5418' }}>✎</span>
          <div className="main"><div className="t">{t.name} <span className="role-tag" style={{ marginLeft: 6 }}>{t.category}</span></div><div className="s">{t.subject}</div></div>
          {isAdmin && <React.Fragment>
            <button className="btn btn-ghost btn-sm" onClick={() => startEdit(t)}>Edit</button>
            <button className="btn btn-ghost btn-sm" onClick={() => remove(t.id)}>Delete</button>
          </React.Fragment>}
        </div>)}
      </div>
    </React.Fragment>;
  }

  // ── FILED DOCUMENTS — compose a cover letter for any certificate ─────
  function FiledDocs({ d, onCompose }) {
    const [propertyId, setPropertyId] = useState('');
    const propById = Object.fromEntries(d.props.map(p => [p.id, p]));
    const certs = d.certs.filter(c => (!propertyId || c.property_id === propertyId) && CERT_LABELS[c.type]);
    return <div className="card">
      <div className="card-head">
        <div><h3>Filed documents</h3><div className="sub">Email any filed certificate to the property's current tenant — a cover letter is drafted for you.</div></div>
        <select className="pm-input" style={{ padding: '7px 10px', fontSize: 13 }} value={propertyId} onChange={e => setPropertyId(e.target.value)}>
          <option value="">All properties</option>
          {d.props.map(p => <option key={p.id} value={p.id}>{p.address}</option>)}
        </select>
      </div>
      {certs.length === 0 ? <div className="empty"><div className="ico">▤</div><h3>No documents on file</h3><p>Certificates you upload appear here, ready to send on to tenants.</p></div>
        : certs.map(c => {
          const p = propById[c.property_id] || {};
          return <div className="row" key={c.id}>
            <span className="avatar">{(CERT_LABELS[c.type] || '?')[0]}</span>
            <div className="main">
              <div className="t">{CERT_LABELS[c.type] || c.type}{c.cert_number ? ' · ' + c.cert_number : ''}</div>
              <div className="s">{p.address || '—'}{c.inspected_date ? ' · dated ' + fmt(c.inspected_date) : ''}{c.expiry_date ? ' · expires ' + fmt(c.expiry_date) : ''}</div>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={() => onCompose({ templateId: 'lt6', propertyId: c.property_id, fill: { '[DOCUMENT]': CERT_LABELS[c.type] || c.type } })}>Compose ✉</button>
          </div>;
        })}
    </div>;
  }

  // ── SENT — append-only audit log ─────────────────────────────────────
  function SentLog({ sent }) {
    if (sent === null) return <div className="card card-pad"><Spin /></div>;
    if (sent.length === 0) return <div className="card"><div className="empty"><div className="ico">✉</div><h3>Nothing sent yet</h3><p>Every approved message — email, SMS or WhatsApp — is logged here with its full text, an audit trail you can rely on.</p></div></div>;
    const chMeta = { email: ['Email', 'cm-channel-email'], sms: ['SMS', 'cm-channel-sms'], whatsapp: ['WhatsApp', 'cm-channel-whatsapp'] };
    return <div className="card">
      {sent.map((s, i) => {
        const dt = s.details || {};
        const cm = chMeta[dt.channel] || chMeta.email;
        return <div className="row" key={i}>
          <span className="avatar" style={{ background: 'var(--ok-soft)', color: 'var(--ok)' }}>✓</span>
          <div className="main">
            <div className="t">{dt.subject || s.action}</div>
            <div className="s">{dt.template ? dt.template + ' · ' : ''}{dt.tenant || dt.to || ''}{dt.property ? ' · ' + dt.property : ''}</div>
          </div>
          <span className={'cm-via-pill on ' + cm[1]}>{cm[0]}</span>
          <span className="pmd-mono cm-hint">{fmtT(s.at)}</span>
        </div>;
      })}
    </div>;
  }

  function CommsStyles() {
    return <style>{`
      .cm-grid { display: grid; grid-template-columns: 300px 1fr; gap: 16px; align-items: start; }
      .cm-grid > .card { margin-top: 0; }
      .cm-side .field input, .cm-side .field select { width: 100%; }
      .cm-ctx { margin-top: 4px; padding: 11px 13px; background: var(--surface-2); border-radius: var(--radius-sm); font-size: 13px; }
      .cm-ctx-l { font-size: 9.5px; color: var(--ink-faint); letter-spacing: .06em; }
      .cm-preview { border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff; }
      .cm-preview-meta { padding: 13px 16px; border-bottom: 1px solid var(--line-soft); font-size: 13px; display: flex; flex-direction: column; gap: 4px; }
      .cm-preview-body { padding: 16px; white-space: pre-wrap; line-height: 1.65; font-size: 14px; }
      .cm-actions { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
      .cm-hint { font-size: 10.5px; color: var(--ink-faint); }
      .cm-tokens { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 4px 0 14px; }
      .cm-token { font-size: 11px; padding: 4px 10px; border: 1px solid var(--line2); border-radius: 999px; background: var(--surface-2); cursor: pointer; font-weight: 600; color: var(--ink-soft); }
      .cm-token:hover { border-color: var(--brand-deep); color: var(--brand-deep); }
      .cm-channels { display: flex; gap: 6px; }
      .cm-channel { flex: 1; padding: 8px 6px; border: 1.5px solid var(--line); background: var(--surface); border-radius: 9px; font-size: 12.5px; font-weight: 700; color: var(--ink-faint); cursor: pointer; }
      .cm-channel:hover { border-color: var(--ink-faint); }
      .cm-channel.on { color: #fff; border-color: transparent; }
      .cm-channel-email.on { background: #2a6fdb; }
      .cm-channel-sms.on { background: #7a4a8a; }
      .cm-channel-whatsapp.on { background: #1f8a4c; }
      .cm-via-pill { font-size: 10.5px; font-weight: 700; padding: 2px 9px; border-radius: 999px; color: #fff; margin-left: 6px; white-space: nowrap; }
      .cm-via-pill.cm-channel-email { background: #2a6fdb; }
      .cm-via-pill.cm-channel-sms { background: #7a4a8a; }
      .cm-via-pill.cm-channel-whatsapp { background: #1f8a4c; }
      @media (max-width: 860px) { .cm-grid { grid-template-columns: 1fr; } }
    `}</style>;
  }

  window.PMCommunicateHub = CommunicateHub;
})();
