// Contacts — ranked candidate contacts for confirmed brands.
// Only shows for brands with confirmedProperties.length > 0 (not generic-none, not stopped).

function Contacts({ brands, selectedBrandId, onSelectBrand, onNavigate, onToast, onDiscoverContacts, onUpdateBrand, currentUser, onMoveToResearch }) {
  const [searchingId, setSearchingId] = React.useState(null);
  const [confirm, confirmEl] = useConfirm();

  const runDiscovery = async (id) => {
    if (!onDiscoverContacts || searchingId) return;
    setSearchingId(id);
    onToast('Searching for decision-makers…');
    try { await onDiscoverContacts(id); }
    catch (e) { /* toast handled upstream */ }
    finally { setSearchingId(null); }
  };

  // Edit / add / delete contacts — saved straight onto the brand.
  const updateContacts = (brand, next) => onUpdateBrand && onUpdateBrand(brand.id, { contacts: next });
  const saveContact = (brand, idx, patch) =>
    updateContacts(brand, (brand.contacts || []).map((c, i) => (i === idx ? { ...c, ...patch } : c)));
  const deleteContact = (brand, idx) =>
    updateContacts(brand, (brand.contacts || []).filter((_, i) => i !== idx));
  const addContact = (brand) => {
    const blank = { name: '', title: '', company: brand.brand, seniority: 'Manager', region: '',
      email: '', email_guessed: false, confidence: 60, why: 'Added manually.', source: 'Manual entry',
      linkedin: '', _new: true };
    updateContacts(brand, [blank, ...(brand.contacts || [])]);
  };

  // Eligible: a property is confirmed and the brand is still in the flow.
  // pbConfirmed + pbIsHeldOrClosed are the shared definitions — the hand-rolled
  // version here only checked priority, so a brand REJECTED after its property
  // was confirmed (status 'not-relevant' / researchStatus 'rejected', priority
  // untouched) stayed in this To-do list showing a "Rejected" chip, and the tab
  // count disagreed with the sidebar badge, which uses needsContactWork.
  const eligible = brands.filter((b) => !pbIsHeldOrClosed(b) && pbConfirmed(b).length > 0);

  // Shared with Research, Match and Draft — one filter setting for the flow.
  const f = useBrandFilters(eligible);
  // A guessed email counts as unfinished, not done — the app's own contact
  // discovery is unreliable, so "has a contact" is not the same as "can email".
  const [gate, setGate] = usePersistentState('contacts.gate', 'todo'); // todo | missing | unverified | ready | all
  const counts = React.useMemo(() => {
    const c = { missing: 0, unverified: 0, ready: 0 };
    eligible.forEach((b) => { c[contactState(b)] += 1; });
    return { ...c, todo: c.missing + c.unverified, all: eligible.length };
  }, [eligible]);

  const filteredEligible = React.useMemo(() => eligible.filter((b) => {
    if (!f.matches(b)) return false;
    const st = contactState(b);
    if (gate === 'todo')  return st !== 'ready';
    if (gate === 'all')   return true;
    return st === gate;
  }), [eligible, f.matches, gate]);

  // Missing first, then unverified, then by tier — worst-off at the top.
  const stateRank = { missing: 0, unverified: 1, ready: 2 };
  const tierOrder = { 'tier-1': 0, 'tier-2': 1, 'tier-3': 2 };
  const sorted = [...filteredEligible].sort((a, b) => {
    const sa = stateRank[contactState(a)], sb = stateRank[contactState(b)];
    if (sa !== sb) return sa - sb;
    return (tierOrder[a.priority] ?? 9) - (tierOrder[b.priority] ?? 9);
  });

  const selected = brands.find((b) => b.id === selectedBrandId && eligible.some((e) => e.id === b.id)) || sorted[0];

  React.useEffect(() => {
    if (selected && (!selectedBrandId || !eligible.some((e) => e.id === selectedBrandId))) {
      onSelectBrand(selected.id);
    }
  }, []);

  if (!selected) {
    return (
      <div className="page page-cap">
        <SectionHeader
          eyebrow="Step 04 · Find & rank contacts"
          title="Contacts"
          sub="Brands arrive here once you have confirmed a property for them."
        />
        <Card lg>
          <div className="empty">
            <div style={{ fontWeight: 600, color: 'var(--ink)' }}>
              {f.active ? 'No brands match these filters.' : 'Nothing is ready for contacts yet.'}
            </div>
            <div className="text-sm" style={{ marginTop: 6, maxWidth: 460, marginInline: 'auto', lineHeight: 1.6 }}>
              {f.active
                ? 'The shared tier / category / assignee / country filters are hiding every confirmed brand.'
                : 'Confirm a property match for a brand and it appears here. Then add the decision-maker by hand — the AI is not reliable at finding real people.'}
            </div>
            <div className="row gap-2" style={{ marginTop: 14, justifyContent: 'center' }}>
              {f.active ? (
                <button className="btn btn-sm btn-primary" onClick={f.clear}>Clear filters</button>
              ) : (
                <button className="btn btn-sm btn-primary" onClick={() => onNavigate('match')}>
                  Open Property Match
                  <window.I.arrowRight size={12} />
                </button>
              )}
            </div>
          </div>
        </Card>
      </div>
    );
  }

  return (
    <div className="page page-cap">
      {/* The sub-line says what the To-do number means. Most of these brands
          were signed off at the earlier gates, and that decision was about
          relevance and which property — not about whether anyone here can be
          emailed. That is why a brand sits in To do having been reviewed. */}
      <SectionHeader
        eyebrow="Step 04 · Find & rank contacts"
        title="Contacts"
        sub={`${counts.missing} ${counts.missing === 1 ? 'brand has' : 'brands have'} nobody on record and ${counts.unverified} `
          + `${counts.unverified === 1 ? 'has' : 'have'} only a guessed address. Being reviewed earlier does not clear this: `
          + `that sign-off was about relevance and the property, not about who to email. The AI is not reliable at finding real `
          + `people, so add the decision-maker by hand where it matters most.`}
      />

      <div className="responsive-split">
        <Card pad={false} style={{ position: 'sticky', top: 80, maxHeight: 'calc(100vh - 120px)', overflowY: 'auto' }}>
          <div style={{ padding: '12px 14px', borderBottom: '1px solid var(--hairline)' }}>
            <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start' }}>
              <div>
                <div className="eyebrow">Confirmed brands</div>
                <div className="text-xs text-3" style={{ marginTop: 4 }}>
                  {counts.todo} need a contact · {counts.ready} ready
                </div>
              </div>
              <button
                className="btn btn-icon btn-ghost"
                onClick={() => f.setOpen((x) => !x)}
                title="Filter brands"
                style={{ color: f.active ? 'var(--accent)' : 'var(--ink-3)' }}
              >
                <window.I.filter size={14} />
              </button>
            </div>
          </div>
          <StageFilterBar
            value={gate}
            onChange={setGate}
            options={[
              { id: 'todo',       label: 'To do',      count: counts.todo,       title: 'Anything that cannot be emailed yet — no contact, or a guessed address' },
              { id: 'missing',    label: 'Missing',    count: counts.missing,    title: 'No contact on record at all — research the decision-maker' },
              { id: 'unverified', label: 'Unverified', count: counts.unverified, title: 'Contact exists but the email is guessed — confirm before sending' },
              { id: 'ready',      label: 'Ready',      count: counts.ready,      title: 'At least one contact with a verified email' },
              { id: 'all',        label: 'All',        count: counts.all },
            ]}
          />
          {f.open ? <BrandFilterPanel f={f} /> : null}
          {sorted.map((b) => {
            const isSel = selected.id === b.id;
            return (
              <div
                key={b.id}
                onClick={() => onSelectBrand(b.id)}
                style={{
                  padding: '12px 14px',
                  borderBottom: '1px solid var(--hairline)',
                  cursor: 'pointer',
                  background: isSel ? 'var(--accent-soft-bg)' : 'transparent',
                  borderLeft: isSel ? '3px solid var(--accent)' : '3px solid transparent',
                }}
              >
                <div className="row gap-2" style={{ alignItems: 'center' }}>
                  <BrandLogo brand={b} size={22} />
                  <span style={{ fontWeight: 600, fontSize: 13.5 }}>{b.brand}</span>
                  <span className="right text-xs text-3 mono">{(b.contacts || []).length}</span>
                </div>
                <div className="text-xs text-3 truncate" style={{ marginTop: 4, paddingLeft: 30 }}>
                  → {b.confirmedProperties.map((p) => p === 'generic' ? 'Generic' : propLabel(p)).join(', ')}
                </div>
                <div className="row gap-1" style={{ marginTop: 6, paddingLeft: 30, flexWrap: 'wrap' }}>
                  <BrandStatusChip brand={b} onNavigate={onNavigate} />
                </div>
              </div>
            );
          })}
          {sorted.length === 0 ? (
            <div className="empty" style={{ padding: '28px 16px' }}>
              <div>No brands match these filters.</div>
              <button className="btn btn-sm" style={{ marginTop: 10 }} onClick={f.clear}>Clear filters</button>
            </div>
          ) : null}
        </Card>

        <div className="col gap-3">
          <Card>
            <div className="row gap-3">
              <BrandLogo brand={selected} size={38} radius={9} />
              <div className="grow">
                <div className="row gap-2">
                  <span className="h3">{selected.brand}</span>
                  <span className="text-3 text-sm">· {selected.category}</span>
                </div>
                <div className="text-2 text-sm" style={{ marginTop: 2 }}>
                  Outreach target: {selected.confirmedProperties.map((p) => p === 'generic' ? 'Generic portfolio' : propName(p)).join(', ')}
                </div>
              </div>
              <div className="col gap-1" style={{ alignItems: 'flex-end' }}>
                <span className="eyebrow text-xs">Outreach stage</span>
                <select
                  value={selected.outreachStatus || 'not_contacted'}
                  onChange={async (e) => {
                    const next = e.target.value;
                    if (onUpdateBrand && await guardOutreachChange(selected, next, confirm)) {
                      onUpdateBrand(selected.id, { outreachStatus: next });
                    }
                  }}
                  className="field"
                  style={{ height: 30, padding: '0 8px', fontSize: 12.5, color: outreachMeta(selected.outreachStatus).color }}
                >
                  {OUTREACH_STAGES.map((s) => (
                    <option key={s.value} value={s.value}>{s.label}</option>
                  ))}
                </select>
              </div>
              <button className="btn btn-sm btn-primary" onClick={() => onNavigate('draft', selected.id)}>
                Open draft
                <window.I.arrowRight size={12} />
              </button>
            </div>
          </Card>

          {/* Tier / status / relationship / outreach / assignee / actions — the
              same strip on every stage. */}
          <BrandControlBar
            brand={selected}
            onUpdateBrand={onUpdateBrand}
            onMoveToResearch={onMoveToResearch}
            onToast={onToast}
            currentUser={currentUser}
          />

          {/* Notes — same panel, same position as every other stage: directly
              under the brand header. Reads and writes the one shared list. */}
          {onUpdateBrand ? (
            <NotesPanel
              brand={selected}
              currentUser={currentUser}
              onUpdateBrand={onUpdateBrand}
              onToast={onToast}
            />
          ) : null}

          <Card pad={false}>
            <div className="row" style={{ padding: '14px 18px', borderBottom: '1px solid var(--hairline)', justifyContent: 'space-between' }}>
              <div>
                <div className="h3">Candidate contacts</div>
                <div className="text-3 text-xs" style={{ marginTop: 2 }}>
                  {(selected.contacts || []).length > 0
                    ? `${(selected.contacts || []).length} people, ranked by relevance to the outreach.`
                    : 'Decision-makers in brand, marketing, partnerships and growth.'}
                </div>
              </div>
              <div className="row gap-2">
                <button className="btn btn-sm" onClick={() => addContact(selected)}>
                  <window.I.plus size={12} />
                  Add contact
                </button>
                {(selected.contacts || []).length > 0 ? (
                  <button
                    className="btn btn-sm btn-ghost"
                    onClick={() => runDiscovery(selected.id)}
                    disabled={searchingId === selected.id}
                  >
                    <window.I.refresh size={12} className={searchingId === selected.id ? 'spin' : ''} />
                    {searchingId === selected.id ? 'Searching…' : 'Re-run search'}
                  </button>
                ) : null}
              </div>
            </div>

            {(selected.contacts || []).length === 0 ? (
              <div className="empty">
                {searchingId === selected.id ? (
                  <div className="col gap-2" style={{ alignItems: 'center' }}>
                    <window.I.refresh size={22} className="spin" stroke="var(--accent)" />
                    <div>Searching for decision-makers…</div>
                    <div className="text-xs text-3">This can take up to a minute.</div>
                  </div>
                ) : (
                  <div className="col gap-3" style={{ alignItems: 'center' }}>
                    <div>No contacts discovered yet.</div>
                    <button className="btn btn-primary btn-sm" onClick={() => runDiscovery(selected.id)}>
                      <window.I.users size={13} />
                      Find contacts
                    </button>
                  </div>
                )}
              </div>
            ) : (
              <div className="col" style={{ padding: 14, gap: 12 }}>
                {(selected.contacts || []).map((c, i) => (
                  <ContactCard
                    key={i}
                    c={c}
                    rank={i + 1}
                    brand={selected}
                    onSave={(patch) => saveContact(selected, i, patch)}
                    onDelete={() => deleteContact(selected, i)}
                    onToast={onToast}
                    confirm={confirm}
                  />
                ))}
              </div>
            )}
          </Card>

          {(selected.contacts || []).length > 0 ? (
            <Card>
              <div className="eyebrow row gap-1" style={{ marginBottom: 8 }}>
                <window.I.sparkle size={11} stroke="var(--accent)" />
                Why these contacts
              </div>
              <div className="text-2 text-sm" style={{ lineHeight: 1.6 }}>
                For Tier {selected.priority?.replace('tier-', '') || '–'} outreach we prioritise C-suite + brand/marketing/partnerships heads. {(selected.contacts || []).filter((c) => c.email && !c.email_guessed).length} of {(selected.contacts || []).length} contacts have a verified email; {(selected.contacts || []).filter((c) => c.email_guessed).length} are best-guess patterns to confirm before sending. The rest will need a LinkedIn message or routed introduction.
              </div>
            </Card>
          ) : null}
        </div>
      </div>
      {confirmEl}
    </div>
  );
}

const CARD_SENIORITY = ['C-Suite', 'VP', 'Head of', 'Director', 'Manager'];

function ContactCard({ c, rank, brand, onSave, onDelete, onToast, confirm }) {
  const [expanded, setExpanded] = React.useState(false);
  const [editing, setEditing] = React.useState(!!c._new);
  const [form, setForm] = React.useState(() => ({ ...c }));
  React.useEffect(() => { if (!editing) setForm({ ...c }); }, [c.name, c.title, c.email, c.seniority, c.region, c.linkedin]);

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const copyEmail = (e) => {
    e.stopPropagation();
    if (c.email && navigator.clipboard) navigator.clipboard.writeText(c.email);
    onToast && onToast('Email copied');
  };

  const save = () => {
    if (!(form.name || '').trim()) { onToast && onToast('Name is required'); return; }
    const emailChanged = (form.email || '') !== (c.email || '');
    onSave({
      name: form.name.trim(),
      title: (form.title || '').trim(),
      email: (form.email || '').trim() || null,
      seniority: CARD_SENIORITY.includes(form.seniority) ? form.seniority : 'Manager',
      region: (form.region || '').trim(),
      linkedin: (form.linkedin || '').trim() || null,
      // A user-entered email is real, not a guess.
      email_guessed: emailChanged ? false : c.email_guessed,
      _new: undefined,
    });
    setEditing(false);
    onToast && onToast('Contact saved');
  };

  const cancel = () => {
    if (c._new) { onDelete(); return; }  // discard an unsaved new row
    setForm({ ...c });
    setEditing(false);
  };

  const fieldStyle = {
    width: '100%', height: 32, padding: '0 8px', fontSize: 13,
    border: '1px solid var(--hairline-strong)', borderRadius: 6,
    background: 'var(--card)', color: 'var(--ink)',
  };

  // ─── Edit mode ───────────────────────────────────────────────────────────
  if (editing) {
    return (
      <div className="card" style={{ padding: 14, border: '1px solid var(--accent)', borderRadius: 10, background: 'var(--card)' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <div className="col gap-1"><span className="eyebrow text-xs">Name *</span><input style={fieldStyle} value={form.name || ''} onChange={(e) => set('name', e.target.value)} autoFocus /></div>
          <div className="col gap-1"><span className="eyebrow text-xs">Title</span><input style={fieldStyle} value={form.title || ''} onChange={(e) => set('title', e.target.value)} /></div>
          <div className="col gap-1" style={{ gridColumn: '1 / -1' }}><span className="eyebrow text-xs">Email</span><input style={fieldStyle} value={form.email || ''} onChange={(e) => set('email', e.target.value)} placeholder="name@company.com" /></div>
          <div className="col gap-1"><span className="eyebrow text-xs">Seniority</span>
            <select style={fieldStyle} value={CARD_SENIORITY.includes(form.seniority) ? form.seniority : 'Manager'} onChange={(e) => set('seniority', e.target.value)}>
              {CARD_SENIORITY.map((s) => <option key={s} value={s}>{s}</option>)}
            </select>
          </div>
          <div className="col gap-1"><span className="eyebrow text-xs">Region</span><input style={fieldStyle} value={form.region || ''} onChange={(e) => set('region', e.target.value)} /></div>
          <div className="col gap-1" style={{ gridColumn: '1 / -1' }}><span className="eyebrow text-xs">LinkedIn (without https://)</span><input style={fieldStyle} value={form.linkedin || ''} onChange={(e) => set('linkedin', e.target.value)} placeholder="linkedin.com/in/…" /></div>
        </div>
        <div className="row gap-2" style={{ marginTop: 12, justifyContent: 'flex-end' }}>
          <button className="btn btn-sm" onClick={cancel}>Cancel</button>
          <button className="btn btn-sm btn-primary" onClick={save}><window.I.check size={13} /> Save contact</button>
        </div>
      </div>
    );
  }

  // ─── Display mode ────────────────────────────────────────────────────────
  return (
    <div className="card" style={{ padding: 0, border: '1px solid var(--hairline)', borderRadius: 10, overflow: 'hidden', background: 'var(--card)' }}>
      <div className="row gap-3 row-clickable" onClick={() => setExpanded((x) => !x)} style={{ padding: '14px 16px', alignItems: 'center', cursor: 'pointer' }}>
        <span className="mono text-xs text-3" style={{ width: 20, flexShrink: 0 }}>#{rank}</span>
        <Avatar initials={initialsFor(c.name)} size={38} />

        <div className="grow" style={{ minWidth: 0 }}>
          <div className="row gap-2" style={{ alignItems: 'center', flexWrap: 'wrap' }}>
            <span style={{ fontWeight: 600, fontSize: 14 }}>{c.name || 'Unnamed contact'}</span>
            {c.seniority ? <Pill kind="muted">{c.seniority}</Pill> : null}
          </div>
          <div className="text-sm text-2 truncate" style={{ marginTop: 2 }}>{c.title || '—'}</div>
          <div className="row gap-2 text-xs text-3" style={{ marginTop: 3, flexWrap: 'wrap' }}>
            {c.company ? <span>{c.company}</span> : null}
            {c.region ? (<><span>·</span><span>{c.region}</span></>) : null}
          </div>
        </div>

        <div className="col gap-2" style={{ alignItems: 'flex-end', flexShrink: 0 }}>
          <div className="row gap-2" style={{ alignItems: 'center' }}>
            <span className="text-xs text-3">Fit</span>
            <div style={{ width: 80 }}><ScoreBar value={c.confidence} /></div>
          </div>
          <div className="row gap-1" onClick={(e) => e.stopPropagation()}>
            {c.email ? (
              <Pill kind={c.email_guessed ? 'pending' : 'positive'} className="row gap-1" style={{ fontSize: 10.5, cursor: 'pointer' }} onClick={copyEmail}
                title={c.email_guessed ? `${c.email} (likely pattern — verify)` : `${c.email} (click to copy)`}>
                <window.I.mail size={10} /> {c.email_guessed ? 'Email (verify)' : 'Email'}
              </Pill>
            ) : (
              <Pill kind="muted" className="row gap-1" style={{ fontSize: 10.5 }}>No email</Pill>
            )}
            {c.linkedin ? (
              <a href={`https://${c.linkedin}`} target="_blank" rel="noopener noreferrer" className="btn btn-icon btn-ghost" title="LinkedIn profile" style={{ color: 'var(--ink-3)' }}>
                <window.I.external size={13} />
              </a>
            ) : null}
            <button className="btn btn-icon btn-ghost" title="Edit contact" onClick={() => setEditing(true)}>
              <window.I.edit size={13} />
            </button>
            <button className="btn btn-icon btn-ghost" title="Delete contact" onClick={async () => {
              if (await confirm({ title: `Remove ${c.name || 'this contact'}?`, message: 'This deletes the contact from the brand.', confirmLabel: 'Remove', danger: true })) onDelete();
            }}>
              <window.I.trash size={13} stroke="var(--negative)" />
            </button>
            <button className="btn btn-icon btn-ghost" title={expanded ? 'Hide detail' : 'Show detail'}>
              <window.I.chevronDown size={14} style={{ transform: expanded ? 'rotate(180deg)' : 'none', transition: 'transform 120ms' }} />
            </button>
          </div>
        </div>
      </div>

      {expanded ? (
        <div style={{ padding: '14px 16px 16px', borderTop: '1px solid var(--hairline)', background: 'var(--card-alt)' }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
            <div>
              <div className="eyebrow" style={{ marginBottom: 6 }}>Why this contact</div>
              <div className="text-sm text-2" style={{ lineHeight: 1.55 }}>{c.why || 'No rationale recorded.'}</div>
            </div>
            <div>
              <div className="eyebrow" style={{ marginBottom: 6 }}>Source &amp; email</div>
              <div className="text-sm text-2">{c.source || 'Not specified.'}</div>
              {c.email ? (
                <div className="col gap-1" style={{ marginTop: 8 }}>
                  <button className="row gap-2 text-xs mono" onClick={copyEmail} title="Click to copy" style={{ background: 'transparent', border: 0, padding: 0, cursor: 'pointer', color: 'var(--ink-2)' }}>
                    <window.I.mail size={11} />
                    <span>{c.email}</span>
                    <window.I.copy size={11} stroke="var(--ink-3)" />
                  </button>
                  {c.email_guessed ? (
                    <span className="text-xs" style={{ color: 'var(--pending)' }}>Best-guess pattern (first.last@domain). Verify before sending.</span>
                  ) : (
                    <span className="text-xs" style={{ color: 'var(--positive)' }}>Found online or entered manually.</span>
                  )}
                </div>
              ) : (
                <div className="text-xs text-3" style={{ marginTop: 8 }}>No email yet. Click edit to add one, or reach out via LinkedIn.</div>
              )}
            </div>
          </div>
        </div>
      ) : null}
    </div>
  );
}

window.Contacts = Contacts;
