// Contacts list view — every person/household, lifecycle stage, history
const LIFECYCLE_META = {
  new_contact:        { label: 'New Contact',        color: 'oklch(0.55 0.02 90)',  tone: 'gray',    desc: 'Captured details, no outcome marked yet' },
  new_lead:           { label: 'New Lead',           color: 'oklch(0.5 0.1 240)',   tone: 'blue',    desc: 'Right client — not ready to proceed' },
  qualified:          { label: 'Qualifies',          color: 'oklch(0.55 0.12 145)', tone: 'green',   desc: 'Right client — appointment made' },
  in_pipeline:        { label: 'In Pipeline',        color: 'oklch(0.55 0.12 70)',  tone: 'amber',   desc: 'Has an active quote' },
  customer_active:    { label: 'Active Customer',    color: 'oklch(0.5 0.12 155)',  tone: 'green',   desc: 'Past customer with new active deal' },
  past_customer:      { label: 'Past Customer',      color: 'oklch(0.45 0.1 155)',  tone: 'green',   desc: 'Bought before, no current activity' },
  lost_quote:         { label: 'Lost (was quoted)',  color: 'oklch(0.55 0.14 28)',  tone: 'red',     desc: 'Got a quote but didn\u2019t buy' },
  no_sale:            { label: 'Did Not Qualify',    color: 'oklch(0.5 0.04 28)',   tone: 'red',     desc: 'Marked as not the right client' },
  dormant_new_lead:   { label: 'Dormant Lead',       color: 'oklch(0.55 0.02 90)',  tone: 'gray',    desc: '>1 year cold — re-engagement' },
  dormant_qualified:  { label: 'Dormant (qualified)', color: 'oklch(0.55 0.02 90)', tone: 'gray',    desc: '>1 year cold — was qualified' },
  dormant_past_customer: { label: 'Dormant Customer', color: 'oklch(0.55 0.02 90)', tone: 'gray',   desc: '>1 year since last buy — gold for re-engagement' },
  dormant_lost_quote: { label: 'Dormant (lost)',     color: 'oklch(0.55 0.02 90)',  tone: 'gray',    desc: '>1 year since lost quote' },
};

function lifecycleMeta(stage) {
  return LIFECYCLE_META[stage] || { label: stage, color: 'var(--ink-3)', tone: 'gray', desc: '' };
}

function LifecyclePill({ stage }) {
  const m = lifecycleMeta(stage);
  return (
    <span className="pill" style={{
      background: `color-mix(in oklch, ${m.color} 12%, var(--surface))`,
      color: m.color,
      border: `1px solid color-mix(in oklch, ${m.color} 20%, transparent)`,
      whiteSpace: 'nowrap',
    }}>
      <span className="dot" style={{ background: m.color }}></span>
      {m.label}
    </span>
  );
}

function Contacts({ contacts, deals, reps, showrooms, showroomScope, currentUser, openContact, openDeal, onNewContact, onClaimLead, externalSearch }) {
  const [sort, setSort] = React.useState({ key: 'lastContact', dir: 'desc' });
  const [localSearch, setSearch] = React.useState('');
  const search = externalSearch || localSearch;
  const [lifecycleFilter, setLifecycleFilter] = React.useState('all');
  const [ownerFilter, setOwnerFilter] = React.useState('all');
  const [dateField, setDateField] = React.useState('firstContact');
  const [dateFrom, setDateFrom] = React.useState('');
  const [dateTo, setDateTo] = React.useState('');
  const [missingOnly, setMissingOnly] = React.useState(false);

  // === ACCESS LAYER (applied first, irrespective of filters) ===
  // Salespeople only see contacts they own, or unassigned contacts at their showroom
  let accessible = contacts;
  if (currentUser.role === 'sales_designer') {
    accessible = accessible.filter(c => (currentUser.works || []).includes(c.homeShowroom));
  } else if (currentUser.role === 'manager') {
    accessible = accessible.filter(c => (currentUser.manages || []).includes(c.homeShowroom));
  }

  // === KPI BASE: showroom-scoped + owner-filtered, BEFORE lifecycle/search filters ===
  // (so the KPI cards reflect the audience you're looking at, not what's narrowed in the table)
  let kpiBase = accessible;
  if (showroomScope !== 'all') kpiBase = kpiBase.filter(c => c.homeShowroom === showroomScope);
  if (ownerFilter !== 'all') kpiBase = kpiBase.filter(c => c.owner === ownerFilter);
  // Date range filter
  if (dateFrom || dateTo) {
    kpiBase = kpiBase.filter(c => {
      const d = c[dateField];
      if (!d) return false;
      if (dateFrom && d < dateFrom) return false;
      if (dateTo && d > dateTo) return false;
      return true;
    });
  }

  // Inbound unclaimed leads (from marketing channels) visible to this user,
  // narrowed to the showroom currently selected in the top switcher
  const unclaimedLeads = accessible.filter(c =>
    c.inbound && !c.owner &&
    (showroomScope === 'all' || c.homeShowroom === showroomScope)
  );

  // === TABLE ROWS: KPI base + lifecycle/search filters ===
  let rows = kpiBase;
  if (lifecycleFilter !== 'all') {
    if (lifecycleFilter === 'active') rows = rows.filter(c => ['new_lead','qualified','in_pipeline','new_contact','customer_active'].includes(c.lifecycle));
    else if (lifecycleFilter === 'customers') rows = rows.filter(c => ['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle));
    else if (lifecycleFilter === 'dormant') rows = rows.filter(c => c.lifecycle.startsWith('dormant_'));
    else if (lifecycleFilter === 'quoted') rows = rows.filter(c => (c.dealCount || 0) > 0);
    else rows = rows.filter(c => c.lifecycle === lifecycleFilter);
  }
  // Missing both phone AND email
  const hasPhone = (c) => !!(c.phone && c.phone.toString().trim());
  const hasEmail = (c) => !!(c.email && c.email.toString().trim());
  if (missingOnly) rows = rows.filter(c => !hasPhone(c) && !hasEmail(c));
  if (search) {
    const q = search.toLowerCase();
    const qDigits = q.replace(/\D/g, '');
    rows = rows.filter(c =>
      c.name.toLowerCase().includes(q) ||
      (qDigits && (c.phone || '').replace(/\D/g, '').includes(qDigits)) ||
      (c.email || '').toLowerCase().includes(q) ||
      (c.suburb || '').toLowerCase().includes(q)
    );
  }

  rows = [...rows].sort((a, b) => {
    let va = a[sort.key], vb = b[sort.key];
    if (sort.key === 'lastContact' || sort.key === 'firstContact') {
      va = va || '0000-00-00'; vb = vb || '0000-00-00';
    }
    if (va == null) va = '';
    if (vb == null) vb = '';
    if (typeof va === 'string') return sort.dir === 'asc' ? va.localeCompare(vb) : vb.localeCompare(va);
    return sort.dir === 'asc' ? va - vb : vb - va;
  });

  const setSortKey = (key) => {
    setSort(prev => prev.key === key
      ? { key, dir: prev.dir === 'asc' ? 'desc' : 'asc' }
      : { key, dir: ['lastContact','ltv','dealCount','daysSinceContact'].includes(key) ? 'desc' : 'asc' }
    );
  };
  const SortHdr = ({ k, label, num }) => (
    <th className={`sortable ${num ? 'num-col' : ''}`} onClick={() => setSortKey(k)}>
      {label}
      <span className="sort-ind">{sort.key === k ? (sort.dir === 'asc' ? '▲' : '▼') : '↕'}</span>
    </th>
  );

  // KPIs (over the filtered scope but lifecycle-filter independent)
  const total = kpiBase.length;
  const newThisMonth = kpiBase.filter(c => {
    if (!c.firstContact) return false;
    return c.firstContact.startsWith('2026-05');
  }).length;
  const customers = kpiBase.filter(c => c.lifecycle === 'past_customer' || c.lifecycle === 'customer_active' || c.lifecycle === 'dormant_past_customer').length;
  const dormant = kpiBase.filter(c => c.lifecycle.startsWith('dormant_')).length;
  const repeatable = kpiBase.filter(c => c.lifecycle === 'dormant_past_customer').length;
  const totalLeadOutcome = kpiBase.filter(c => c.lifecycle === 'new_lead' || c.lifecycle === 'qualified' || c.lifecycle === 'in_pipeline' || c.lifecycle === 'past_customer' || c.lifecycle === 'customer_active' || c.lifecycle === 'lost_quote').length;
  const totalSold = kpiBase.filter(c => c.lifecycle === 'past_customer' || c.lifecycle === 'customer_active' || c.lifecycle === 'dormant_past_customer').length;
  const conversionRate = totalLeadOutcome > 0 ? totalSold / totalLeadOutcome : 0;

  // Owners for filter dropdown — only owners present in the user's accessible set
  const owners = [...new Set(accessible.map(c => c.owner).filter(Boolean))].sort();
  const missingCount = kpiBase.filter(c => !hasPhone(c) && !hasEmail(c)).length;

  return (
    <div>
      {unclaimedLeads.length > 0 && (
        <div className="card" style={{ marginBottom: 16, border: '1px solid oklch(0.8 0.1 250)', background: 'oklch(0.97 0.03 250)' }}>
          <div style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid oklch(0.88 0.04 250)' }}>
            <div style={{ width: 30, height: 30, borderRadius: 15, background: 'oklch(0.5 0.12 250)', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Icon name="alert" size={15} color="white"/>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13, fontWeight: 600, color: 'oklch(0.35 0.12 250)' }}>
                {unclaimedLeads.length} new lead{unclaimedLeads.length > 1 ? 's' : ''} from marketing — unclaimed
              </div>
              <div style={{ fontSize: 11.5, color: 'oklch(0.4 0.08 250)' }}>Claim one to take ownership and start working it.</div>
            </div>
          </div>
          <div style={{ padding: 10, display: 'flex', flexDirection: 'column', gap: 6 }}>
            {unclaimedLeads.map(c => (
              <div key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 6 }}>
                <Avatar name={c.name} sz="sm"/>
                <div style={{ flex: 1, minWidth: 0, cursor: 'pointer' }} onClick={() => openContact(c)}>
                  <div style={{ fontSize: 12.5, fontWeight: 500 }}>{c.name}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-3)', display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                    <span>{c.suburb}</span>
                    <span>·</span>
                    <span>{showrooms.find(s => s.id === c.homeShowroom)?.name}</span>
                    {c.entries && c.entries[0] && <span style={{ fontStyle: 'italic' }}>· {c.entries[0].comments.slice(0, 60)}</span>}
                  </div>
                </div>
                <span className="pill" style={{ background: 'oklch(0.93 0.05 250)', color: 'oklch(0.4 0.12 250)', fontSize: 10.5 }}>
                  <span className="dot" style={{ background: 'oklch(0.5 0.12 250)' }}></span>
                  {c.channel || c.primarySource}
                </span>
                {onClaimLead && (
                  <button className="btn sm primary" onClick={() => onClaimLead(c.id)}>
                    <Icon name="check" size={11}/> Claim
                  </button>
                )}
              </div>
            ))}
          </div>
        </div>
      )}
      <div className="grid-kpis">
        {(() => {
          const card = (active, onClick, node) => <div className={`kpi-click ${active ? 'on' : ''}`} onClick={onClick}>{node}{active && <span className="kpi-filtering">Filtering</span>}</div>;
          return (
            <React.Fragment>
              {card(lifecycleFilter === 'all' && !missingOnly, () => { setLifecycleFilter('all'); setMissingOnly(false); }, <Kpi label="Total contacts" value={fmt.num(total)} foot={`${newThisMonth} new this month`} deltaPct={0.058}/>)}
              {card(lifecycleFilter === 'customers', () => setLifecycleFilter(p => p === 'customers' ? 'all' : 'customers'), <Kpi label="Active customers" value={customers} foot={`${repeatable} dormant — re-engage`} deltaPct={0.04}/>)}
              {card(lifecycleFilter === 'quoted', () => setLifecycleFilter(p => p === 'quoted' ? 'all' : 'quoted'), <Kpi label="Contact → Sale" value={fmt.pct(conversionRate, 1)} foot="reached a quote" deltaPct={0.012}/>)}
              {card(lifecycleFilter === 'dormant', () => setLifecycleFilter(p => p === 'dormant' ? 'all' : 'dormant'), <Kpi label="Dormant pool" value={dormant} foot="cold leads · potential re-prospect" deltaPct={-0.03} alert={dormant > 100}/>)}
            </React.Fragment>
          );
        })()}
      </div>

      <div className="card">
        <div className="card-hd">
          <div>
            <div className="card-title">
              <span className="num">{rows.length}</span> contacts
            </div>
            <div className="card-sub">5 years of contact history · click a row for the full timeline</div>
          </div>
          <div style={{ display: 'flex', gap: 6 }}>
            <button
              className="btn sm"
              onClick={() => downloadCSV('contacts-' + new Date().toISOString().slice(0, 10) + '.csv', rows, [
                { key: 'id', label: 'Contact ID' },
                { key: 'name', label: 'Name' },
                { key: 'phone', label: 'Phone' },
                { key: 'email', label: 'Email' },
                { key: 'suburb', label: 'Suburb' },
                { key: 'homeShowroom', label: 'Showroom', format: (v) => showrooms.find(s => s.id === v)?.name || v },
                { key: 'owner', label: 'Owner designer' },
                { key: 'lifecycle', label: 'Lifecycle stage', format: (v) => lifecycleMeta(v).label },
                { key: 'primarySource', label: 'Source' },
                { key: 'primaryMeans', label: 'First contact channel' },
                { key: 'dealCount', label: 'Number of quotes' },
                { key: 'ltv', label: 'Lifetime value AUD' },
                { key: 'firstContact', label: 'First contact date' },
                { key: 'lastContact', label: 'Last contact date' },
                { key: 'daysSinceContact', label: 'Days since last touch' },
              ])}
            >
              <Icon name="download" size={12}/> Export
            </button>
            <button className="btn sm primary" onClick={onNewContact}><Icon name="plus" size={12}/> New contact</button>
          </div>
        </div>

        <div className="filters-bar" style={{ borderTop: 0 }}>
          <div className="topbar-search" style={{ marginLeft: 0 }}>
            <span className="search-icon"><Icon name="search" size={12}/></span>
            <input
              placeholder="Search name, phone, email, suburb…"
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              style={{ width: 240 }}
            />
          </div>
          <select className="sel" value={lifecycleFilter} onChange={(e) => setLifecycleFilter(e.target.value)}>
            <option value="all">All lifecycle stages</option>
            <option value="active">Active (in pipeline + new leads)</option>
            <option value="customers">Customers (past + active)</option>
            <option value="dormant">Dormant (re-engage)</option>
            <option value="new_contact">— New Contact</option>
            <option value="new_lead">— New Lead</option>
            <option value="qualified">— Qualifies</option>
            <option value="in_pipeline">— In Pipeline</option>
            <option value="customer_active">— Active Customer</option>
            <option value="past_customer">— Past Customer</option>
            <option value="lost_quote">— Lost</option>
            <option value="no_sale">— Did Not Qualify</option>
            <option value="dormant_past_customer">— Dormant Customer</option>
            <option value="dormant_qualified">— Dormant Lead</option>
          </select>
          <select className="sel" value={ownerFilter} onChange={(e) => setOwnerFilter(e.target.value)} style={currentUser.role === 'sales_designer' ? { display: 'none' } : null}>
            <option value="all">All owners</option>
            <option value="">— Unassigned</option>
            {owners.map(o => <option key={o} value={o}>{o}</option>)}
          </select>
          <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 11, color: 'var(--ink-3)' }}>
            <select className="sel" value={dateField} onChange={(e) => setDateField(e.target.value)} style={{ fontSize: 11 }}>
              <option value="firstContact">First contact</option>
              <option value="lastContact">Last contact</option>
            </select>
            <input type="date" value={dateFrom} onChange={(e) => setDateFrom(e.target.value)} style={{ padding: '4px 6px', border: '1px solid var(--border)', borderRadius: 4, fontSize: 11 }}/>
            <span>→</span>
            <input type="date" value={dateTo} onChange={(e) => setDateTo(e.target.value)} style={{ padding: '4px 6px', border: '1px solid var(--border)', borderRadius: 4, fontSize: 11 }}/>
          </div>
          <button
            className={`filter-chip ${missingOnly ? 'active' : ''}`}
            onClick={() => setMissingOnly(m => !m)}
            title="Show only contacts missing BOTH a phone number and an email address"
          >
            <Icon name="alert" size={11}/> Missing phone &amp; email
            {missingCount > 0 && <span className="num" style={{ marginLeft: 3, opacity: 0.85 }}>{missingCount}</span>}
            {missingOnly && <span className="x">×</span>}
          </button>
          {(search || lifecycleFilter !== 'all' || ownerFilter !== 'all' || dateFrom || dateTo || missingOnly) && (
            <button
              className="filter-chip"
              onClick={() => { setSearch(''); setLifecycleFilter('all'); setOwnerFilter('all'); setDateFrom(''); setDateTo(''); setMissingOnly(false); }}
              style={{ marginLeft: 4 }}
            >
              <Icon name="close" size={11}/> Clear filters
            </button>
          )}
        </div>

        <div className="tbl-wrap" style={{ maxHeight: 'calc(100vh - 320px)', overflowY: 'auto' }}>
          <table className="tbl">
            <thead>
              <tr>
                <SortHdr k="name" label="Contact"/>
                <SortHdr k="lifecycle" label="Lifecycle"/>
                <SortHdr k="primarySource" label="Source"/>
                <SortHdr k="owner" label="Owner"/>
                <SortHdr k="dealCount" label="Quotes" num/>
                <SortHdr k="ltv" label="Lifetime $" num/>
                <SortHdr k="lastContact" label="Last contact" num/>
                <SortHdr k="daysSinceContact" label="Idle" num/>
              </tr>
            </thead>
            <tbody>
              {rows.slice(0, 500).map(c => (
                <tr key={c.id} onClick={() => openContact(c)}>
                  <td>
                    <div className="customer-cell">
                      <Avatar name={c.name} sz="sm"/>
                      <div className="customer-meta">
                        <div className="customer-name">{c.name}</div>
                        <div className="customer-sub" style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                          {c.phone
                            ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}><Icon name="phone" size={10}/>{c.phone}</span>
                            : <span className="missing-detail"><Icon name="phone" size={10}/>no phone</span>}
                          {c.email
                            ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3, maxWidth: 180, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}><Icon name="mail" size={10}/>{c.email}</span>
                            : <span className="missing-detail"><Icon name="mail" size={10}/>no email</span>}
                          {c.suburb && <span style={{ color: 'var(--ink-3)' }}>· {c.suburb}</span>}
                        </div>
                      </div>
                    </div>
                  </td>
                  <td><LifecyclePill stage={c.lifecycle}/></td>
                  <td style={{ fontSize: 12, color: 'var(--ink-2)' }}>{c.primarySource || '—'}</td>
                  <td style={{ fontSize: 12, color: 'var(--ink-2)' }}>{c.owner || '—'}</td>
                  <td className="num-col"><span className="num">{c.dealCount}</span></td>
                  <td className="num-col"><span className="num" style={{ fontWeight: c.ltv > 0 ? 500 : 400, color: c.ltv > 0 ? 'var(--won)' : 'var(--ink-3)' }}>{c.ltv > 0 ? fmt.money(c.ltv, true) : '—'}</span></td>
                  <td className="num-col" style={{ color: 'var(--ink-3)' }}>
                    <span className="num">{c.lastContact ? fmt.shortDate(c.lastContact) : '—'}</span>
                  </td>
                  <td className={`num-col age-cell ${c.daysSinceContact < 30 ? 'fresh' : c.daysSinceContact < 90 ? 'warm' : 'hot'}`}>
                    <span className="num">
                      {c.daysSinceContact < 30 ? c.daysSinceContact + 'd' :
                       c.daysSinceContact < 365 ? Math.floor(c.daysSinceContact / 30) + 'mo' :
                       Math.floor(c.daysSinceContact / 365) + 'y'}
                    </span>
                  </td>
                </tr>
              ))}
              {rows.length === 0 && (
                <tr><td colSpan="8" className="empty">No contacts match.</td></tr>
              )}
            </tbody>
          </table>
          {rows.length > 500 && (
            <div style={{ padding: 12, textAlign: 'center', fontSize: 12, color: 'var(--ink-3)' }}>
              Showing first 500 of {rows.length} contacts — narrow your filter to see more.
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

window.Contacts = Contacts;
window.LifecyclePill = LifecyclePill;
window.lifecycleMeta = lifecycleMeta;
window.LIFECYCLE_META = LIFECYCLE_META;
