// ============================================================
//   LayoutSurfaces — "Two surfaces: Inbox + Pipeline" (closed loop)
//   Surface 1 "Action needed": attention. Drains to zero.
//   Surface 2 "Your pipeline": tracking. Persistent, you own it.
//
//   CLOSED LOOP: resolving an inbox item files the candidate into
//   a pipeline stage (with a confirmation) — and they stay there.
//   Pipeline rows are actionable: move stage, no bouncing back to
//   the inbox. Attention and tracking are independent but linked.
// ============================================================
const { color: SUC } = window.FF;

const STAGES = [
  { key:'engaged',      label:'Engaged' },
  { key:'reviewed',     label:'Reviewed' },
  { key:'interviewing', label:'Interviewing' },
  { key:'offer',        label:'Offer' },
  { key:'hired',        label:'Hired' },
];
const STAGE_LABEL = STAGES.reduce((a, s) => { a[s.key] = s.label; return a; }, {});

// Which candidate.stage values are "qualified / in play" — they live in your
// pipeline (Surface 2) and start in the column below.
const QUALIFIED_TO_COLUMN = { engaged:'engaged', review:'reviewed', interview:'interviewing', hired:'hired' };
// What the recruiter needs to do for each, shown as an inbox item (Surface 1).
const INBOX_ASK = {
  engaged:   { ask:'Replied — confirm & schedule',  verb:'Schedule',         to:'interviewing', toast:'scheduled → Interviewing' },
  review:    { ask:'Scored — ready for your review', verb:'Advance',          to:'interviewing', toast:'advanced to Interviewing' },
  interview: { ask:'Qualified — review & advance',   verb:'Advance to offer', to:'offer',        toast:'advanced to Offer' },
};
const prFor = (score) => (score >= 4 ? 1 : score >= 3 ? 2 : 3);
// AI is handling these in the background (Surface 3).
const AI_STAGES = new Set(['sourcing','new']);

function LayoutSurfaces({ candidates, selectedId, onSelect, vocab }) {
  const byId = React.useMemo(() => { const m = {}; candidates.forEach(c => m[c.id] = c); return m; }, [candidates]);
  const [surface, setSurface] = React.useState('inbox');
  const f = useFilterBar();
  const [foldOpen, setFoldOpen] = React.useState(false);
  const [resolved, setResolved] = React.useState({});

  // Qualified candidates seed the pipeline at their current stage; the same
  // people surface in the inbox until you act on them (the closed loop).
  const qualified = React.useMemo(
    () => candidates.filter(c => QUALIFIED_TO_COLUMN[c.stage]), [candidates]);
  const seedPlacement = React.useMemo(() => {
    const m = {}; qualified.forEach(c => { m[c.id] = QUALIFIED_TO_COLUMN[c.stage]; }); return m;
  }, [qualified]);

  const [placement, setPlacement] = React.useState(seedPlacement);

  // Handling clears the trigger and drops the row out of Action needed. The
  // candidate does NOT move — they stay exactly where they are in Pipeline,
  // which is the whole handle-vs-disposition distinction.
  const handle = (id, t) => {
    setResolved(r => ({ ...r, [id]: true }));
    setToast(`${(byId[id] ? byId[id].name.split(' ')[0] : 'Candidate')} — ${t.action.toLowerCase()} logged`);
    window.clearTimeout(handle._t);
    handle._t = window.setTimeout(() => setToast(null), 2400);
  };
  const [toast, setToast] = React.useState(null);

  // Single-sourced from atTrigger(). This used to be "every candidate in
  // engaged/review/interview", which counted people merely awaiting a reply,
  // missed new qualified inbound entirely, and ignored handled/stale — i.e. it
  // reproduced the Engaged-pile problem the ladder exists to fix, while the tab
  // claimed to be Action needed.
  const allInbox = React.useMemo(() => candidates
    .map(c => ({ c, t: atTrigger(c) }))
    .filter(x => x.t)
    .sort((a, b) => a.t.rank - b.t.rank || a.t.sort - b.t.sort || (b.c.score || 0) - (a.c.score || 0))
    .map(({ c, t }) => ({ id:c.id, pr: t.rank === 1 ? 1 : t.rank === 4 ? 3 : 2, ask: t.reason, cat: t.cat, age: t.age })),
    [candidates]);

  const unresolved = allInbox.filter(i => !resolved[i.id]);
  // Source + secondary scope the inbox, but off-filter items FOLD rather than
  // disappear — a filter must never silently suppress attention.
  const openInbox   = unresolved.filter(i => f.matchSource(byId[i.id]) && f.matchSecondary(byId[i.id]));
  const foldedInbox = unresolved.filter(i => !(f.matchSource(byId[i.id]) && f.matchSecondary(byId[i.id])));
  const otherSource = f.source === 'applied' ? 'sourced' : 'applied';
  const needsYou = new Set(unresolved.map(i => i.id));

  // The AI's working pool: everyone it's still sourcing / screening / contacting
  // in the background. Reviewable here, but quieter than the two hero surfaces.
  // Graduation: anyone with a live trigger belongs to the human now, so they
  // leave the machine room rather than appearing in both.
  const aiWorking = candidates.filter(c => AI_STAGES.has(c.stage) && !atTrigger(c));



  return (
    <React.Fragment>
      <FilterBar f={f}/>
      <SurfaceSwitch surface={surface} setSurface={setSurface} inboxCount={openInbox.length}/>
      {toast && <Toast msg={toast}/>}
      <div style={{ flex:1, overflow:'auto', borderTop:`1px solid ${SUC.tint[80]}` }}>
        {surface === 'inbox' && (
          <React.Fragment>
            <InboxSurface items={openInbox} byId={byId} selectedId={selectedId} onSelect={onSelect} vocab={vocab} onHandle={handle}/>
            <FoldedAttention n={foldedInbox.length} otherSource={`${otherSource} items`} open={foldOpen} onToggle={() => setFoldOpen(o => !o)}>
              <InboxSurface items={foldedInbox} byId={byId} selectedId={selectedId} onSelect={onSelect} vocab={vocab}/>
            </FoldedAttention>
          </React.Fragment>
        )}
        {surface === 'pipeline' && <PipelineSurface byId={byId} placement={placement} needsYou={needsYou} selectedId={selectedId} onSelect={onSelect} aiWorking={aiWorking.filter(f.matchSource)} filter={f} vocab={vocab}/>}
      </div>
    </React.Fragment>
  );
}

function Toast({ msg }) {
  return (
    <div style={{
      margin:'8px 18px 0', padding:'9px 13px', borderRadius:8,
      background: SUC.shade[880], color:'#fff', fontSize:12.5, fontWeight:600,
      display:'flex', alignItems:'center', gap:8,
      animation:'su-toast .2s ease',
    }}>
      <style>{`@keyframes su-toast{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}`}</style>
      <span style={{ color: SUC.primary[300] }}>✓</span>{msg}
    </div>
  );
}

function SurfaceSwitch({ surface, setSurface, inboxCount }) {
  const tab = (k, label, sub, count, accent) => {
    const on = surface === k;
    return (
      <button onClick={() => setSurface(k)} style={{
        flex:1, minWidth:0, padding:'9px 11px', textAlign:'left',
        background: on ? '#fff' : 'transparent',
        border: on ? `1px solid ${accent}` : '1px solid transparent',
        borderRadius:10, cursor:'pointer', boxShadow: on ? '0 1px 3px rgba(13,10,44,.08)' : 'none',
      }}>
        <div style={{ fontSize:13.5, fontWeight:800, color: on ? accent : SUC.shade[820], whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{label}</div>
        {/* Count rides the sub-line: at 460px the label row can't hold a badge
            as well without clipping, and the sub-line was underused. */}
        <div style={{ fontSize:10.5, color: SUC.shade[700], whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', marginTop:1 }}>
          {count != null && <span style={{ fontWeight:800, color: accent, fontVariantNumeric:'tabular-nums' }}>{count} </span>}
          {sub}
        </div>
      </button>
    );
  };

  return (
    <div style={{ padding:'14px 18px 10px', display:'flex', alignItems:'center' }}>
      <div style={{ flex:1, minWidth:0, display:'flex', gap:4, background: SUC.tint[20], border:`1px solid ${SUC.tint[80]}`, borderRadius:12, padding:4 }}>
        {tab('inbox', 'Action needed', "· what's blocked on you", inboxCount, SUC.primary[600])}
        {tab('pipeline', 'Your pipeline', 'Where everyone is', null, SUC.shade[840])}
      </div>
    </div>
  );
}

// Kinds in the AI activity feed → dot color + summary label.
const AI_KIND = {
  sourced:   { c: SUC.copilot[500],   label:'Sourced' },
  messaged:  { c: SUC.inform[500],    label:'Messaged' },
  replied:   { c: SUC.primary[500],   label:'Replied' },
  scheduled: { c: SUC.highlight[500], label:'Scheduled' },
};

// Sub-status shown on each AI-working row, keyed by candidate stage.
const AI_STATUS = {
  sourcing: { label:'Sourcing',   c: SUC.copilot[600] },
  new:      { label:'Screening',  c: SUC.inform[700] },
  engaged:  { label:'Contacting', c: SUC.highlight[600] },
};

/* ---------- SURFACE 3 — AI WORKING (reviewable, de-emphasized) ----------
   The candidates the AI is handling in the background. Rows are quieter than
   the inbox/pipeline (muted type, smaller avatar, no action buttons — these
   aren't yours to action yet) but fully reviewable: click any row to open it
   in the same detail panel. */
function AIWorkingSurface({ list, selectedId, onSelect }) {
  const ai = (typeof window !== 'undefined' && window.__AI_ACTIVITY__) || { thisWeek:{}, recent:[] };
  const tw = ai.thisWeek || {};
  const stats = [['sourced','Sourced'],['messaged','Messaged'],['replied','Replied'],['scheduled','Scheduled']]
    .map(([k, label]) => ({ k, label, n: tw[k] }))
    .filter(s => s.n != null);

  return (
    <div>
      {/* quiet context header + this-week summary */}
      <div style={{ padding:'12px 18px', background: SUC.copilot[50] + '55', borderBottom:`1px solid ${SUC.tint[60]}` }}>
        {stats.length > 0 && (
          <div style={{ display:'flex' }}>
            {stats.map((s, i) => (
              <div key={s.k} style={{ flex:1, textAlign:'center', borderLeft: i ? `1px solid ${SUC.tint[80]}` : 'none' }}>
                <div style={{ fontSize:15, fontWeight:800, color: SUC.shade[820], fontVariantNumeric:'tabular-nums', lineHeight:1.1 }}>{s.n}</div>
                <div style={{ fontSize:9, fontWeight:700, textTransform:'uppercase', letterSpacing:'.05em', color: SUC.shade[600], marginTop:2 }}>{s.label}</div>
              </div>
            ))}
          </div>
        )}
      </div>

      {/* reviewable, de-emphasized candidate rows */}
      {list.length === 0 && (
        <div style={{ padding:'40px 24px', textAlign:'center', fontSize:13, color: SUC.shade[600] }}>
          The AI isn’t working anyone in the background right now.
        </div>
      )}
      {list.map(c => {
        const st = AI_STATUS[c.stage] || { label:'In progress', c: SUC.shade[600] };
        const on = c.id === selectedId;
        return (
          <div key={c.id} onClick={() => onSelect?.(c.id)} style={{
            position:'relative', padding:'9px 16px', borderBottom:`1px solid ${SUC.tint[60]}`, cursor:'pointer',
            background: on ? SUC.highlight[50] + '66' : '#fff', display:'flex', alignItems:'center', gap:10,
          }}>
            {on && <div style={{ position:'absolute', left:0, top:0, bottom:0, width:3, background: SUC.highlight[500] }}/>}
            <span style={{ width:26, height:26, borderRadius:'50%', background: SUC.tint[40], color: SUC.shade[700], display:'flex', alignItems:'center', justifyContent:'center', fontSize:10.5, fontWeight:700, flex:'none' }}>{c.name.split(' ').map(s => s[0]).slice(0, 2).join('')}</span>
            <div style={{ flex:1, minWidth:0 }}>
              <div style={{ fontSize:13, fontWeight:600, color: SUC.shade[820], whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{c.name}</div>
              <div style={{ fontSize:11.5, color: SUC.shade[600], whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{c.role} · {c.loc.split(',')[0]}</div>
            </div>
            <span style={{ display:'inline-flex', alignItems:'center', gap:5, flex:'none', fontSize:10.5, fontWeight:700, color: st.c }}>
              <span style={{ width:6, height:6, borderRadius:'50%', background: st.c }}/>{st.label}
            </span>
            <span style={{ color: SUC.shade[500], fontSize:15, flex:'none', lineHeight:1 }}>›</span>
          </div>
        );
      })}

      <AIActivityDisclosure recent={ai.recent || []}/>
    </div>
  );
}

// Collapsed log of what the AI has done lately. Secondary to the candidate
// list above — folded away by default so the review list stays the focus.
function AIActivityDisclosure({ recent }) {
  const [open, setOpen] = React.useState(false);
  if (!recent.length) return null;
  return (
    <div style={{ borderTop:`1px solid ${SUC.tint[80]}` }}>
      <button onClick={() => setOpen(o => !o)} aria-expanded={open} style={{
        width:'100%', textAlign:'left', display:'flex', alignItems:'center', gap:8,
        padding:'11px 16px', background:'transparent', border:0, cursor:'pointer',
        fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'.06em', color: SUC.shade[600],
      }}>
        Recent activity<span style={{ flex:1 }}/><span style={{ fontSize:10 }}>{open ? '▲' : '▼'}</span>
      </button>
      {open && recent.map((r, i) => {
        const k = AI_KIND[r.kind] || { c: SUC.shade[500] };
        return (
          <div key={i} style={{ display:'flex', gap:10, padding:'8px 16px', alignItems:'flex-start', borderTop:`1px solid ${SUC.tint[60]}` }}>
            <span style={{ width:7, height:7, borderRadius:'50%', background: k.c, marginTop:5, flex:'none' }}/>
            <div style={{ flex:1, minWidth:0 }}>
              <div style={{ fontSize:12.5, color: SUC.shade[820], lineHeight:1.35 }}>{r.text}</div>
              <div style={{ fontSize:10.5, color: SUC.shade[600], marginTop:1 }}>{r.meta} · {r.t}</div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ---------- SURFACE 1 — ACTION NEEDED ----------
   Uses the shared production candidate row rather than a bespoke one, so the
   cards match the shipped panel (name + time / role at company / score · job ·
   provenance pill) and stay in step with the other layouts. The trigger reason
   rides along as the row's opt-in fourth line. */
function InboxSurface({ items, byId, selectedId, onSelect, vocab, onHandle }) {
  if (!items.length) {
    return (
      <div style={{ padding:'48px 24px', textAlign:'center' }}>
        <div style={{ fontSize:40, marginBottom:10 }}>✓</div>
        <div style={{ fontSize:16, fontWeight:800, color: SUC.shade[880], marginBottom:4 }}>Nothing blocked on you.</div>
        <div style={{ fontSize:13, color: SUC.shade[800] }}>Everyone is still in your pipeline — switch tabs to see where.</div>
      </div>
    );
  }
  const list = items.map(i => byId[i.id]).filter(Boolean);
  return <CandidateList items={list} selectedId={selectedId} onSelect={onSelect}
    showSourceBadge vocab={vocab} triggerFor={atTrigger} onHandle={onHandle}/>;
}

/* ---------- SURFACE 2 — PIPELINE ----------
   Mike's notes on this surface, applied:
     - scores were missing → now the shared production row, which brings the
       graded score badge and provenance pill with it
     - the per-row stage dropdown is redundant with the stage control in the
       detail panel, and bulk actions run off the checkboxes → removed
     - "i like it less when there are hundreds of candidates in a stage" →
       every stage collapses, and long ones cap with a "+N more" reveal
   The Action-needed tag he liked stays, as the row's trigger line. */
const PS_CAP = 25;

function PipelineSurface({ byId, placement, needsYou, selectedId, onSelect, aiWorking, filter, vocab }) {
  const keep = (id) => !filter || (filter.matchSource(byId[id]) && filter.matchSecondary(byId[id]));
  const grouped = STAGES.map(s => ({ ...s, ids: Object.keys(placement).filter(id => placement[id] === s.key && keep(id)) }));
  const [open, setOpen] = React.useState(() => new Set(STAGES.map(s => s.key)));
  const [expanded, setExpanded] = React.useState({});
  const toggle = (k) => setOpen(o => { const n = new Set(o); n.has(k) ? n.delete(k) : n.add(k); return n; });

  return (
    <div style={{ padding:'0 0 20px' }}>
      {grouped.map(stage => {
        const isOpen = open.has(stage.key);
        const all    = stage.ids.map(id => byId[id]).filter(Boolean);
        const shown  = expanded[stage.key] ? all : all.slice(0, PS_CAP);
        const hidden = all.length - shown.length;
        return (
          <div key={stage.key}>
            <button onClick={() => toggle(stage.key)} aria-expanded={isOpen} className="ff-stagehd" style={{
              width:'100%', textAlign:'left', display:'flex', alignItems:'center', gap:9,
              padding:'11px 16px', background:'transparent', border:0,
              borderBottom:`1px solid ${SUC.tint[60]}`, cursor:'pointer', transition:'background .12s',
            }}>
              <span style={{
                width:16, height:16, flex:'none', display:'inline-flex', alignItems:'center', justifyContent:'center',
                borderRadius:4, background: SUC.tint[40], color: SUC.shade[800], fontSize:12, fontWeight:700,
                transition:'transform .15s', transform: isOpen ? 'rotate(90deg)' : 'none',
              }}>›</span>
              <span style={{ fontSize:11.5, fontWeight:800, letterSpacing:'.06em', textTransform:'uppercase', color: SUC.shade[880] }}>{stage.label}</span>
              <span style={{ fontSize:11, fontWeight:700, color: SUC.shade[800], fontVariantNumeric:'tabular-nums' }}>{all.length}</span>
            </button>

            {isOpen && !!all.length && (
              <React.Fragment>
                <CandidateList items={shown} selectedId={selectedId} onSelect={onSelect}
                  showSourceBadge vocab={vocab} triggerFor={atTrigger} triggerStyle="badge"/>
                {hidden > 0 && (
                  <button onClick={() => setExpanded(e => ({ ...e, [stage.key]: true }))} style={{
                    width:'100%', textAlign:'left', padding:'10px 16px', background: SUC.tint[20] + '55',
                    border:0, borderBottom:`1px solid ${SUC.tint[80]}`, cursor:'pointer',
                    fontSize:11.5, fontWeight:700, color: SUC.highlight[700],
                  }}>+ {hidden} more in {stage.label}</button>
                )}
              </React.Fragment>
            )}
            {isOpen && !all.length && (
              <div style={{ padding:'10px 16px', fontSize:11.5, color: SUC.shade[800], fontStyle:'italic' }}>Empty</div>
            )}
          </div>
        );
      })}
      <AIWorkingBlock list={aiWorking || []} selectedId={selectedId} onSelect={onSelect}/>
    </div>
  );
}

/* AI working — collapsed, at the FOOT of the pipeline.

   Deliberately last rather than first. The AI's pool is upstream in funnel
   terms, so funnel logic would put it on top — but that is exactly what makes
   it read as "step one of my workflow", which is the confusion this whole
   direction exists to remove. What's actually being expressed is an OWNERSHIP
   boundary: above are the stages you own, below is what you don't own yet.

   Safe to bury here only because Pipeline sits behind a tab, so the attention
   surface is structurally protected. The count stays on the header because
   this layout has no graduation — nothing pulls a candidate out of the AI pool
   automatically, so being findable still matters. */
function AIWorkingBlock({ list, selectedId, onSelect }) {
  const [open, setOpen] = React.useState(false);
  if (!list.length) return null;
  return (
    <div style={{ marginTop:10, borderTop:`6px solid ${SUC.tint[20]}` }}>
      <button onClick={() => setOpen(o => !o)} aria-expanded={open} style={{
        width:'100%', textAlign:'left', display:'flex', alignItems:'center', gap:9,
        padding:'12px 18px', background:'transparent', border:0, cursor:'pointer',
      }}>
        <span style={{
          width:16, height:16, flex:'none', display:'inline-flex', alignItems:'center', justifyContent:'center',
          borderRadius:4, background: SUC.copilot[50], color: SUC.copilot[700], fontSize:12, fontWeight:700,
          transition:'transform .15s', transform: open ? 'rotate(90deg)' : 'none',
        }}>›</span>
        <img src="assets/icons/lightning-20.svg" width={13} height={13} style={{ flex:'none' }}/>
        <span style={{ fontSize:11.5, fontWeight:800, letterSpacing:'.04em', textTransform:'uppercase', color: SUC.copilot[800] }}>AI working</span>
        <span style={{ fontSize:11, fontWeight:800, color:'#fff', background: SUC.copilot[600], borderRadius:9999, padding:'1px 7px', fontVariantNumeric:'tabular-nums' }}>{list.length}</span>
        <span style={{ fontSize:10.5, color: SUC.shade[600] }}>· not yours yet</span>
      </button>
      {open && list.map(c => {
        const st = AI_STATUS[c.stage] || { label:'In progress', c: SUC.shade[600] };
        const on = c.id === selectedId;
        return (
          <div key={c.id} onClick={() => onSelect?.(c.id)} style={{
            position:'relative', padding:'9px 18px 9px 30px', borderTop:`1px solid ${SUC.tint[60]}`, cursor:'pointer',
            background: on ? SUC.highlight[50] + '66' : SUC.tint[20] + '66', display:'flex', alignItems:'center', gap:10,
          }}>
            {on && <div style={{ position:'absolute', left:0, top:0, bottom:0, width:3, background: SUC.highlight[500] }}/>}
            <span style={{ width:26, height:26, borderRadius:'50%', flex:'none', background: SUC.tint[40], color: SUC.shade[700], display:'flex', alignItems:'center', justifyContent:'center', fontSize:10.5, fontWeight:700 }}>{c.name.split(' ').map(x=>x[0]).slice(0,2).join('')}</span>
            <div style={{ flex:1, minWidth:0 }}>
              <div style={{ fontSize:13, fontWeight:600, color: SUC.shade[820], whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{c.name}</div>
              <div style={{ fontSize:11.5, color: SUC.shade[600], whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{c.role} · {(c.loc||'').split(',')[0]}</div>
            </div>
            <span style={{ display:'inline-flex', alignItems:'center', gap:5, flex:'none', fontSize:10.5, fontWeight:700, color: st.c }}>
              <span style={{ width:6, height:6, borderRadius:'50%', background: st.c }}/>{st.label}
            </span>
          </div>
        );
      })}
    </div>
  );
}

Object.assign(window, { LayoutSurfaces, STAGES, QUALIFIED_TO_COLUMN, INBOX_ASK, prFor, AI_STAGES, AIWorkingSurface });
