/* ELEV8 ATS - screens: Voice ops hub, Call result detail, Engagements & care. */

const DSo = window.Stand8DesignSystem_b5c975;

const OUTCOMES = {
  qualified: { label: 'Qualified', tone: 'success' },
  callback: { label: 'Callback requested', tone: 'info' },
  not_interested: { label: 'Not interested', tone: 'neutral' },
  no_answer: { label: 'No answer', tone: 'warning' },
};

/* ============================== VOICE OPS HUB ============================== */
function VoiceOpsScreen() {
  const D = window.E8DATA;
  const { showToast, density } = React.useContext(E8Ctx);
  const [batches, setBatches] = React.useState(D.batches);
  const toggleBatch = (id) =>
    setBatches(batches.map((b) => (b.id === id ? { ...b, state: b.state === 'running' ? 'paused' : 'running' } : b)));

  return (
    <React.Fragment>
      <Topbar
        crumbs={[{ label: 'Voice ops' }]}
        actions={
          <React.Fragment>
            <DSo.Button variant="ghost" size="sm" icon="tune" iconOnly title="Call settings"></DSo.Button>
            <DSo.Button variant="secondary" size="sm" icon="upload">Export results</DSo.Button>
            <DSo.Button variant="primary" size="sm" icon="add_call">New screening batch</DSo.Button>
          </React.Fragment>
        }
      />
      <div className="e8-content">
        <div className="e8-page">
          {/* Live calls */}
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 10 }}>
            <h3 style={{ fontSize: 'var(--ui-text-md)' }}>Live now</h3>
            <DSo.Badge tone="success" dot>{D.liveCalls.length} calls</DSo.Badge>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, marginBottom: 24 }}>
            {D.liveCalls.map((c) => (
              <DSo.LiveCallCard
                key={c.id}
                candidate={c.candidate}
                job={c.job}
                timer={c.timer}
                sentiment={c.sentiment}
                footer={
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <DSo.AgentChip name="Voice screening" />
                    <span style={{ marginLeft: 'auto', display: 'flex', gap: 4 }}>
                      <DSo.Button variant="ghost" size="sm" icon="headphones" iconOnly title="Listen in"></DSo.Button>
                      <DSo.Button variant="ghost" size="sm" icon="stop_circle" iconOnly title="End call" onClick={() => showToast(`Call with ${c.candidate} ended`, 'Undo')}></DSo.Button>
                    </span>
                  </div>
                }
              />
            ))}
          </div>

          {/* Batches */}
          <h3 style={{ fontSize: 'var(--ui-text-md)', marginBottom: 10 }}>Batches</h3>
          <div style={{ marginBottom: 24 }}>
            <ConfigurableTable listKey="voice-batches" primary={false}
              density="compact"
              registry={[
                { key: 'name', label: 'Batch', render: (b) => (
                  <span>
                    <span style={{ fontWeight: 500 }}>{b.name}</span>
                    <span style={{ display: 'block', fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>{b.agent}</span>
                  </span>
                ) },
                { key: 'progress', label: 'Progress', width: 230, render: (b) => (
                  b.state === 'scheduled'
                    ? <span style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)' }}>Starts {b.at}</span>
                    : (
                      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, width: 210 }}>
                        <DSo.Progress value={(b.done / b.total) * 100} soft={b.state === 'paused'} />
                        <span className="tnum" style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)', whiteSpace: 'nowrap' }}>{b.done}/{b.total}</span>
                      </span>
                    )
                ) },
                { key: 'state', label: 'State', width: 110, render: (b) => (
                  <DSo.Badge tone={b.state === 'running' ? 'success' : b.state === 'paused' ? 'neutral' : 'info'} dot>
                    {b.state === 'running' ? 'Running' : b.state === 'paused' ? 'Paused' : 'Scheduled'}
                  </DSo.Badge>
                ) },
              ]}
              rows={batches}
              rowActions={(b) => (
                b.state === 'scheduled'
                  ? <DSo.Button variant="ghost" size="sm" icon="play_arrow" onClick={() => showToast('Batch started')}>Start now</DSo.Button>
                  : <DSo.Button variant="ghost" size="sm" icon={b.state === 'paused' ? 'play_arrow' : 'pause'} onClick={() => toggleBatch(b.id)}>{b.state === 'paused' ? 'Resume' : 'Pause'}</DSo.Button>
              )}
            />
          </div>

          {/* Recent results */}
          <h3 style={{ fontSize: 'var(--ui-text-md)', marginBottom: 10 }}>Recent results</h3>
          <ConfigurableTable listKey="voice-results"
            density={density}
            onRowClick={(r) => navigate('voice/' + r.id)}
            registry={[
              { key: 'candidate', label: 'Candidate', render: (r) => <NameCell name={r.candidate} sub={r.job} /> },
              { key: 'outcome', label: 'Outcome', render: (r) => <DSo.Badge tone={OUTCOMES[r.outcome].tone} dot>{OUTCOMES[r.outcome].label}</DSo.Badge> },
              { key: 'summary', label: 'Summary', render: (r) => (
                <span style={{ fontSize: 'var(--ui-text-base)', color: 'var(--ui-text-secondary)', display: 'inline-block', maxWidth: 380, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                  {r.summary[0] || '-'}
                </span>
              ) },
              { key: 'duration', label: 'Duration', num: true },
              { key: 'cost', label: 'Cost', num: true },
              { key: 'when', label: 'When', secondary: true },
            ]}
            rows={D.callResults}
            rowActions={(r) => <DSo.Button variant="ghost" size="sm" icon="arrow_forward">Open</DSo.Button>}
          />
        </div>
      </div>
    </React.Fragment>
  );
}

/* ============================== CALL RESULT DETAIL ============================== */
function CallResultScreen({ id }) {
  const D = window.E8DATA;
  const { showToast } = React.useContext(E8Ctx);
  const r = D.callResults.find((x) => x.id === id);
  if (!r) {
    return (
      <React.Fragment>
        <Topbar crumbs={[{ label: 'Voice ops', to: 'voice' }, { label: 'Not found' }]} />
        <div className="e8-content"><div className="e8-page"><div style={{ maxWidth: 460, margin: '48px auto' }}>
          <DSo.EmptyState icon="phone_missed" title="Call not found" body="This screening call does not exist, or the link is out of date." cta={<DSo.Button variant="primary" icon="graphic_eq" onClick={() => navigate('voice')}>Back to voice ops</DSo.Button>} />
        </div></div></div>
      </React.Fragment>
    );
  }
  const o = OUTCOMES[r.outcome];

  return (
    <React.Fragment>
      <Topbar
        crumbs={[{ label: 'Voice ops', to: 'voice' }, { label: r.candidate + ' · screening call' }]}
        actions={
          <React.Fragment>
            <DSo.Button variant="ghost" size="sm" icon="event">Schedule follow-up</DSo.Button>
            <DSo.Button variant="secondary" size="sm" icon="close" onClick={() => showToast(`${r.candidate} rejected for JO-44219`, 'Undo')}>Reject</DSo.Button>
            <DSo.Button variant="primary" size="sm" icon="send" onClick={() => showToast(`${r.candidate} accepted - draft submission created`, 'Undo')}>Accept → submit to job</DSo.Button>
          </React.Fragment>
        }
      />
      <div className="e8-content">
        <div className="e8-page e8-page-narrow">
          {/* Header */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4, flexWrap: 'wrap' }}>
            <DSo.Avatar name={r.candidate} size="lg" />
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <h1 className="e8-h1">{r.candidate}</h1>
                <DSo.Badge tone={o.tone} dot>{o.label}</DSo.Badge>
              </div>
              <div style={{ fontSize: 'var(--ui-text-base)', color: 'var(--ui-text-secondary)', marginTop: 2 }}>{r.job} · {r.when}</div>
            </div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)', margin: '8px 0 20px', flexWrap: 'wrap' }}>
            <DSo.AgentChip name={r.agent} />
            <span className="tnum">{r.duration}</span>
            <span className="tnum">cost {r.cost}</span>
            <span style={{ color: r.sentiment === 'positive' ? 'var(--ui-success-text)' : 'var(--ui-text-secondary)' }}>sentiment {r.sentiment}</span>
          </div>

          {/* Summary */}
          <div className="e8-card" style={{ padding: 16, marginBottom: 16 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
              <span className="e8-sectionlabel">Screening summary</span>
              <DSo.ProvenanceBadge kind="drafted" />
            </div>
            <ul style={{ margin: 0, paddingLeft: 18, display: 'grid', gap: 6 }}>
              {r.summary.map((s, i) => (
                <li key={i} style={{ fontSize: 'var(--ui-text-base)', lineHeight: 1.5, color: 'var(--ui-text)' }}>{s}</li>
              ))}
            </ul>
          </div>

          {/* Extracted facts */}
          {r.facts.length ? (
            <div style={{ marginBottom: 20 }}>
              <div className="e8-sectionlabel" style={{ marginBottom: 8 }}>Extracted facts</div>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {r.facts.map((f, i) => <DSo.ConfidenceChip key={i} state={f.state}>{f.label}</DSo.ConfidenceChip>)}
              </div>
              <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)', marginTop: 6 }}>
                Confirmed facts were stated on the call; suggested facts need your review before syncing to the record.
              </div>
            </div>
          ) : null}

          {/* Audio */}
          <div className="e8-card" style={{ padding: '12px 16px', marginBottom: 20, display: 'flex', alignItems: 'center', gap: 12 }}>
            <DSo.Button variant="secondary" size="sm" icon="play_arrow" iconOnly title="Play recording"></DSo.Button>
            <DSo.Waveform bars={56} seed={11} style={{ flex: 1, height: 22 }} />
            <span className="tnum" style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)', whiteSpace: 'nowrap' }}>0:00 / {r.duration.replace('m ', ':').replace('s', '')}</span>
          </div>

          {/* Transcript */}
          {r.transcript.length ? (
            <div>
              <div className="e8-sectionlabel" style={{ marginBottom: 6 }}>Transcript</div>
              <div className="e8-card" style={{ padding: '8px 16px' }}>
                {r.transcript.map((t, i) => (
                  <DSo.TranscriptTurn key={i} speaker={t.s} time={t.t} ai={t.ai}>{t.text}</DSo.TranscriptTurn>
                ))}
                <div style={{ textAlign: 'center', padding: '8px 0 10px' }}>
                  <a className="e8-link" style={{ fontSize: 'var(--ui-text-sm)' }}>Show remaining 5m 40s of transcript</a>
                </div>
              </div>
            </div>
          ) : (
            <DSo.EmptyState icon="graphic_eq" title="No transcript for this call" body="Calls under 90 seconds or with no answer don’t produce a transcript." />
          )}
        </div>
      </div>
    </React.Fragment>
  );
}

/* ============================== ENGAGEMENTS & CARE ============================== */
const PO_HEALTH = {
  healthy: { label: 'Healthy', tone: 'success' },
  depleting: { label: 'Depleting', tone: 'warning' },
  expiring: { label: 'Expiring soon', tone: 'warning' },
  expired: { label: 'Expired', tone: 'danger' },
};
const CARE = {
  current: { label: 'Current', tone: 'success' },
  due: { label: 'Due this week', tone: 'warning' },
  overdue: { label: 'Overdue', tone: 'danger' },
};

/* Case studies - the proof stories behind engagements (problem → solution → results). */
function CaseStudyCard({ cs }) {
  const D = window.E8DATA;
  const { showToast } = React.useContext(E8Ctx);
  const eng = D.engagements.find((e) => e.id === cs.id);
  return (
    <div className="e8-cs-card">
      <div className="e8-cs-head">
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 4 }}>
            <DSo.Badge tone="neutral">{cs.industry}</DSo.Badge>
            {cs.status === 'draft' ? <DSo.Badge tone="warning" dot>Draft</DSo.Badge> : <DSo.Badge tone="success" dot>Published</DSo.Badge>}
            <span style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>{cs.date}</span>
          </div>
          <div style={{ fontSize: 'var(--ui-text-lg)', fontWeight: 600, lineHeight: 1.3 }}>{cs.title}</div>
          {eng ? (
            <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)', marginTop: 3 }}>
              <a className="e8-link" tabIndex={0} onKeyDown={window.keyActivate} onClick={() => navigate('consultant/' + eng.cid)}>{eng.consultant}</a> · {eng.role} at {eng.client}
            </div>
          ) : null}
        </div>
      </div>

      <div className="e8-cs-ps">
        <div className="e8-cs-block e8-cs-problem">
          <div className="e8-cs-block-k"><span className="material-symbols-outlined" style={{ fontSize: 13 }}>error</span>Problem</div>
          <div className="e8-cs-block-v">{cs.problem}</div>
        </div>
        <div className="e8-cs-block e8-cs-solution">
          <div className="e8-cs-block-k"><span className="material-symbols-outlined" style={{ fontSize: 13 }}>lightbulb</span>Solution</div>
          <div className="e8-cs-block-v">{cs.solution}</div>
        </div>
      </div>

      {cs.approach && cs.approach.length ? (
        <div>
          <div className="e8-cs-sub">How we did it</div>
          <ul className="e8-cs-approach">
            {cs.approach.map((a, i) => <li key={i}><span className="material-symbols-outlined" style={{ fontSize: 14, color: 'var(--ui-success-text)' }}>check</span>{a}</li>)}
          </ul>
        </div>
      ) : null}

      {cs.results && cs.results.length ? (
        <div className="e8-cs-results">
          {cs.results.map((r, i) => (
            <div key={i} className="e8-cs-result">
              <div className="e8-cs-result-m tnum">{r.metric}</div>
              <div className="e8-cs-result-l">{r.label}</div>
            </div>
          ))}
        </div>
      ) : null}

      {cs.quote ? (
        <div className="e8-cs-quote">
          <span className="material-symbols-outlined e8-cs-quotemark">format_quote</span>
          <div>
            <div className="e8-cs-quote-t">{cs.quote.text}</div>
            <div className="e8-cs-quote-a">{cs.quote.author} · {cs.quote.role}</div>
          </div>
        </div>
      ) : null}

      <div className="e8-cs-foot">
        <span style={{ display: 'flex', flexWrap: 'wrap', gap: 5, flex: 1 }}>{(cs.tags || []).map((t) => <span key={t} className="e8-techchip">{t}</span>)}</span>
        {cs.status === 'draft'
          ? <DSo.Button variant="primary" size="sm" icon="auto_awesome" onClick={() => showToast('Finishing “' + cs.title + '” with ELEV8 - draft updated')}>Finish draft</DSo.Button>
          : (
            <React.Fragment>
              <DSo.Button variant="ghost" size="sm" icon="content_copy" onClick={() => showToast('Case study copied to clipboard')}>Copy</DSo.Button>
              <DSo.Button variant="secondary" size="sm" icon="slideshow" onClick={() => showToast('Added to the ' + cs.industry + ' QBR deck')}>Add to QBR</DSo.Button>
            </React.Fragment>
          )}
      </div>
    </div>
  );
}

function CaseStudiesView() {
  const D = window.E8DATA;
  const { showToast } = React.useContext(E8Ctx);
  const csList = Object.keys(D.caseStudies || {}).map((id) => ({ id, ...D.caseStudies[id] }))
    .sort((a, b) => (a.status === b.status ? 0 : a.status === 'published' ? -1 : 1));
  const published = csList.filter((c) => c.status === 'published').length;
  const drafts = csList.filter((c) => c.status === 'draft').length;
  const industries = new Set(csList.map((c) => c.industry)).size;
  const months = (t) => parseInt(String(t), 10) || 0;
  const ready = D.engagements.filter((e) => !D.caseStudies[e.id] && e.sentiment.tone !== 'danger' && months(e.tenure) >= 6);

  return (
    <div style={{ display: 'grid', gap: 18 }}>
      <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)', maxWidth: 720 }}>
        The proof behind every engagement - a problem, what we did, and the measurable result. Reusable in QBRs, renewals and new-business pitches.
      </div>

      <div className="e8-cfg-stats">
        {[['Published', String(published)], ['In draft', String(drafts)], ['Industries', String(industries)], ['Ready to document', String(ready.length)]].map(([k, v]) => (
          <div key={k} className="e8-cfg-stat"><div className="e8-cfg-stat-k">{k}</div><div className="e8-cfg-stat-v tnum">{v}</div></div>
        ))}
      </div>

      <div className="e8-cs-grid">
        {csList.map((cs) => <CaseStudyCard key={cs.id} cs={cs} />)}
      </div>

      {ready.length ? (
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
            <span className="e8-sectionlabel">Ready to document</span>
            <DSo.ProvenanceBadge kind="advised" />
            <span style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>healthy, long-running engagements with a story worth capturing</span>
          </div>
          <div style={{ display: 'grid', gap: 6 }}>
            {ready.map((e) => (
              <div key={e.id} className="e8-cs-ready">
                <DSo.Avatar name={e.consultant} size="sm" />
                <span style={{ minWidth: 0, flex: 1 }}>
                  <span style={{ display: 'block', fontSize: 'var(--ui-text-base)', fontWeight: 500 }}>{e.consultant} · {e.client}</span>
                  <span style={{ display: 'block', fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>{e.role} · {e.tenure} on billing · sentiment {e.sentiment.label.toLowerCase()}</span>
                </span>
                <DSo.Button variant="secondary" size="sm" icon="auto_awesome" onClick={() => showToast('ELEV8 drafted a case study for ' + e.consultant + ' - review in Approvals', 'Open approvals', () => navigate('approvals'))}>Draft with AI</DSo.Button>
              </div>
            ))}
          </div>
        </div>
      ) : null}
    </div>
  );
}

/* ---- Mobile cards for the post-placement (Engagements) tables ---- */
const OPS_TONE_COLOR = { success: 'var(--ui-success)', warning: 'var(--ui-warning)', danger: 'var(--ui-danger)', info: 'var(--ui-info)', accent: 'var(--ui-accent, var(--ui-accent))', neutral: 'var(--ui-text-tertiary)' };

function EngagementCareCard({ e }) {
  const care = CARE[e.care] || {};
  return (
    <div className="e8-mcard" style={{ cursor: e.cid ? 'pointer' : 'default' }} {...(e.cid ? window.clickableProps(() => navigate('consultant/' + e.cid)) : {})}>
      <span className="e8-mcard-rail" style={{ background: OPS_TONE_COLOR[care.tone] || 'var(--ui-text-tertiary)' }}></span>
      <div className="e8-mcard-body">
        <div className="e8-mcard-top">
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="e8-mcard-name">{e.consultant}</div>
            <div className="e8-mcard-role">{e.role}</div>
          </div>
          <DSo.Badge tone={care.tone || 'neutral'} dot>{care.label || e.care}</DSo.Badge>
        </div>
        <div className="e8-mcard-meta">{e.client} · ends {e.ends} · PO {e.poBurn}%</div>
        <div className="e8-mcard-foot">
          {e.renewal === 'Not due' ? <span style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>Renewal not due</span> : <DSo.Badge tone={e.renewal === 'Escalated' ? 'danger' : 'accent'}>{e.renewal}</DSo.Badge>}
          <span className="e8-mcard-when">{e.lastTouch}</span>
        </div>
      </div>
    </div>
  );
}

function EngagementRenewalCard({ e }) {
  return (
    <div className="e8-mcard" style={{ cursor: 'pointer' }} {...window.clickableProps(() => navigate('consultant/' + e.cid))}>
      <span className="e8-mcard-rail" style={{ background: e.escalated ? 'var(--ui-danger)' : (e.extensionPct < 50 ? 'var(--ui-warning)' : 'var(--ui-success)') }}></span>
      <div className="e8-mcard-body">
        <div className="e8-mcard-top">
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="e8-mcard-name">{e.consultant}</div>
            <div className="e8-mcard-role">{e.role} · {e.client}</div>
          </div>
          <span className="tnum" style={{ fontWeight: 600, fontSize: 'var(--ui-text-base)' }}>{e.renewalValue}</span>
        </div>
        <div className="e8-mcard-meta">ends {e.ends} · PO {e.poBurn}% · {e.extensionPct}% likely</div>
        <div className="e8-mcard-foot">
          {e.escalated ? <DSo.Badge tone="danger" dot>Escalated</DSo.Badge> : <DSo.Badge tone={e.renewalStage > 0 ? 'info' : 'accent'} dot>{e.renewal}</DSo.Badge>}
          <span className="e8-mcard-when"><ActorChip name={e.sponsor} /></span>
        </div>
      </div>
    </div>
  );
}

/* R98: New engagement - staffing placements only (managed programs are set up by ops).
   Consultant picker spans the bench and the candidate pool; margin preview is deterministic
   math from the two rates (labeled computed, not AI). Persists the seed engagement shape and
   assigns the default rep so commissions and TimeHub pick the new cid up without orphaning. */
const ENG_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
/* Only `Mon YYYY` parses (the format every seed `ends` uses and the redeployment radar
   requires); anything else returns '' - the dialog blocks create on it rather than
   persisting a row with a broken end date. */
function engEndsFrom(start, durationMonths) {
  const m = /^([A-Z][a-z]{2})\s+(\d{4})$/.exec(String(start || '').trim());
  if (!m || ENG_MONTHS.indexOf(m[1]) === -1) return '';
  const total = ENG_MONTHS.indexOf(m[1]) + durationMonths;
  return ENG_MONTHS[total % 12] + ' ' + (+m[2] + Math.floor(total / 12));
}
function NewEngagementDialog({ onClose, prefill }) {
  const D = window.E8DATA;
  const { showToast } = React.useContext(E8Ctx);
  const Dialog = window.E8CreateDialog;
  if (!Dialog) return null;
  const pf = prefill || null;

  const parseRate = (v) => parseFloat(String(v || '').replace(/[^0-9.]/g, '')) || 0;
  const durMonths = (v) => { const n = parseInt(String(v == null ? '' : v).replace(/[^0-9]/g, ''), 10); return n > 0 ? n : 0; };

  /* Pickable people: bench first (they are the point), then the candidate pool. */
  const seen = {};
  const people = [];
  (D.bench || []).forEach((b) => { if (!seen[b.id]) { seen[b.id] = 1; people.push({ id: b.id, name: b.name, role: b.role, skills: b.skills || [], bench: true }); } });
  (D.matches || []).forEach((m) => { if (m.name && !seen[m.id]) { seen[m.id] = 1; people.push({ id: m.id, name: m.name, role: m.title, skills: m.skills || [], location: m.location }); } });

  /* R98 T5B: a candidate being placed from a submission is a first-class pick even when they are
     not on the bench or in the live match pool - synthesize a person from the prefill so the
     consultant select shows and defaults to them (a placement, not a bench redeploy). */
  let prefillWho = '';
  if (pf && pf.consultantName) {
    const pid = pf.candId || ('place-' + String(pf.consultantName).toLowerCase().replace(/[^a-z0-9]+/g, '-'));
    prefillWho = pid;
    if (!seen[pid]) { seen[pid] = 1; people.unshift({ id: pid, name: pf.consultantName, role: pf.role, skills: pf.skills || [], location: pf.location, placing: true }); }
  }
  const byId = Object.fromEntries(people.map((p) => [p.id, p]));

  /* Standalone job-order picker source (open orders only) and its lookup. */
  const openJobs = (D.jobs || []).filter((j) => (j.openings || 1) > (j.filled || 0));
  const jobById = Object.fromEntries(openJobs.map((j) => [j.id, j]));

  /* Client options include the prefill client even if it is not yet a CRM row. */
  const clientNames = (D.clients || []).map((c) => c.name);
  if (pf && pf.client && clientNames.indexOf(pf.client) === -1) clientNames.push(pf.client);

  /* Duration options - 3/6/12 plus the prefill's own duration when it differs. */
  const pfDur = pf ? durMonths(pf.duration) : 0;
  const durVals = [3, 6, 12];
  if (pfDur && durVals.indexOf(pfDur) === -1) { durVals.push(pfDur); durVals.sort((a, b) => a - b); }
  const durInitial = String(pfDur || 6);

  /* Bench-first nudge: the soonest roll-off from the radar's shared eligibility helper. */
  const rollOff = window.rdrRollOffs ? window.rdrRollOffs(1)[0] : null;

  const dupFor = (vals) => {
    const p = byId[vals.who];
    return (p && vals.client)
      ? (D.engagements || []).find((e) => e.consultant.toLowerCase() === p.name.toLowerCase() && e.client === vals.client)
      : null;
  };

  /* A rate field is bad once it has content that does not parse to a positive number. */
  const badRate = (v) => String(v || '').trim() && parseRate(v) <= 0;
  const startInvalid = (v) => String(v || '').trim() && !engEndsFrom(v, 1);
  const rateNote = (msg) => <span style={{ color: 'var(--ui-danger-text)' }}>{msg}</span>;

  const fields = [];
  /* Standalone only: an optional job-order seed. Picking one pulls role / client / bill / duration
     from the order, so an engagement can start from a job even with no submission. Hidden in the
     prefill (place-from-submission) flow, which already carries those values. */
  if (!pf) {
    fields.push({ key: 'fromJob', label: 'From job order', type: 'select',
      note: 'Optional - pulls role, client, bill rate and duration from an open order.',
      options: [{ value: '', label: 'Start from scratch…' }].concat(openJobs.map((j) => ({ value: j.id, label: j.id + ' · ' + j.title + ' · ' + j.client }))),
      onPick: (v, { set }) => {
        const j = jobById[v]; if (!j) return;
        if (j.title) set('role', j.title);
        if (j.client) set('client', j.client);
        const br = parseRate(j.rate); if (br > 0) set('bill', String(br));
        const jd = durMonths(j.duration); if (jd) set('duration', String(jd));
      } });
  }
  fields.push(
    { key: 'who', label: 'Consultant', type: 'select', required: true, initial: prefillWho || undefined,
      options: [{ value: '', label: 'Select a consultant…' }].concat(people.map((p) => ({ value: p.id, label: (p.bench ? 'Bench · ' : p.placing ? 'Placing · ' : '') + p.name + ' · ' + (p.role || 'Consultant') }))),
      onPick: (v, { set, vals }) => { const p = byId[v]; if (p && p.role && !(vals.role || '').trim()) set('role', p.role); } },
    { key: 'client', label: 'Client', type: 'select', required: true, initial: (pf && pf.client) || undefined,
      options: [{ value: '', label: 'Select a client…' }].concat(clientNames.map((n) => ({ value: n, label: n }))) },
    { key: 'role', label: 'Role title', placeholder: 'e.g. Data Engineer', required: true, initial: (pf && pf.role) || undefined },
    { key: 'bill', label: 'Bill rate ($/hr)', placeholder: 'e.g. 120', required: true, initial: (pf && pf.bill != null && pf.bill !== '') ? String(pf.bill) : undefined,
      note: (vals) => (badRate(vals.bill) ? rateNote('Enter an hourly bill rate, e.g. 130') : null) },
    { key: 'pay', label: 'Pay rate ($/hr)', placeholder: 'e.g. 88', required: true, initial: (pf && pf.pay != null && pf.pay !== '') ? String(pf.pay) : undefined,
      note: (vals) => (badRate(vals.pay) ? rateNote('Enter an hourly pay rate, e.g. 95') : null) },
    { key: 'start', label: 'Start', placeholder: 'e.g. Jul 2026', initial: (pf && pf.start) || 'Jul 2026',
      note: (vals) => (startInvalid(vals.start) ? rateNote('Format: Jul 2026 - month abbreviation + year') : 'Format: Jul 2026') },
    { key: 'duration', label: 'Duration', type: 'select', initial: durInitial,
      options: durVals.map((n) => ({ value: String(n), label: n + ' months' })) },
  );

  return (
    <Dialog
      title={pf ? 'Create engagement' : 'New engagement'}
      createLabel={pf ? 'Place consultant' : 'Create engagement'}
      onClose={onClose}
      fields={fields}
      footnote="Staffing engagement - managed programs are set up by ops."
      disabled={(vals) => {
        const bill = parseRate(vals.bill), pay = parseRate(vals.pay);
        /* Both rates must parse positive with a real spread, and a non-empty start must parse
           `Mon YYYY` - otherwise the persisted row would break marginOf (divide by 0) or the
           radar's ends parsing. */
        return !!dupFor(vals) || !(bill > 0 && pay > 0 && pay < bill) || startInvalid(vals.start);
      }}
      extra={(vals) => {
        const bill = parseRate(vals.bill), pay = parseRate(vals.pay);
        const dup = dupFor(vals);
        return (
          <React.Fragment>
            {bill > 0 && pay > 0 ? (
              pay >= bill ? (
                <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-danger-text)' }}>Pay rate must be below the bill rate.</div>
              ) : (
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)' }}>
                  <span className="material-symbols-outlined" style={{ fontSize: 15, color: 'var(--ui-success-text)' }}>savings</span>
                  <span style={{ flex: 1 }}>Margin <b className="tnum">{Math.round(((bill - pay) / bill) * 100)}%</b> · <b className="tnum">${Math.round((bill - pay) * 40).toLocaleString()}</b>/wk on 40h <span style={{ color: 'var(--ui-text-tertiary)' }}>· computed from the rates</span></span>
                </div>
              )
            ) : null}
            {dup ? (
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 'var(--ui-text-sm)', color: 'var(--ui-warning-text)' }}>
                <span className="material-symbols-outlined" style={{ fontSize: 15 }}>info</span>
                <span style={{ flex: 1 }}>{dup.consultant} is already engaged at {dup.client}.</span>
                <DSo.Button variant="secondary" size="sm" onClick={() => { onClose(); navigate('consultant/' + dup.cid); }}>Open it</DSo.Button>
              </div>
            ) : null}
            {!vals.who && rollOff ? (
              <div style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>
                Bench-first: {rollOff.consultant} {(window.rdrRolloffLabel ? window.rdrRolloffLabel(rollOff.ends).replace(/^./, (c) => c.toLowerCase()) : 'rolls off ' + rollOff.ends)} - consider redeploying before sourcing new.
              </div>
            ) : null}
          </React.Fragment>
        );
      }}
      onCreate={(v) => {
        const p = byId[v.who];
        if (!p) return;
        onClose();
        const bill = parseRate(v.bill), pay = parseRate(v.pay);
        const start = (v.start || '').trim() || 'Jul 2026';
        const dur = parseInt(v.duration, 10) || 6;
        const rep = ((D.reps || []).find((r) => r.name === D.user.name) || (D.reps || [])[0] || {}).id;
        /* Identity thread: candId comes from the prefill (place-from-submission) or a candidate picked
           straight from the match pool (a c-* id); a bench pick has none - it keeps its cons-* id. */
        const candId = (pf && pf.candId) || (/^c-/.test(p.id) ? p.id : null);
        /* Engagement creation is a replayable, undoable executor (app/store.js): it computes
           cid = 'cons-' + candId, writes the row carrying candId/submissionId/jobId, stamps the
           candidate + submission back-links, records audit + provenance, and fires engagement:created.
           Route-to-consultant + commission ownership (the row's rep field + the live repByCid mirror)
           are preserved inside the executor; res.undo reverts the whole thread. */
        const res = window.e8CreateEngagement ? window.e8CreateEngagement({
          name: p.name, candId: candId, who: p.id,
          submissionId: (pf && pf.submissionId) || null, jobId: (pf && pf.jobId) || null,
          client: v.client, role: v.role.trim(), bill: bill, pay: pay,
          start: start, ends: engEndsFrom(start, dur),
          renewalValue: '$' + Math.round((bill * 40 * 52) / 1000) + 'K',
          location: p.location || (pf && pf.location) || 'Memphis, TN · hybrid',
          skills: (p.skills && p.skills.length ? p.skills : (pf && pf.skills) || []),
          rep: rep,
        }) : null;
        if (!res) { showToast('Create engagement is unavailable in this build'); return; }
        const cid = res.cid;

        if (pf) {
          /* Reversible placement: res.undo drops the engagement + its commission mapping and reverts
             the candidate + submission (+ job) stamps. */
          showToast('Placed ' + p.name + ' at ' + v.client, 'Undo', res.undo);
        } else {
          /* R104 nit E: the standalone path also stamps a candidate back-link (when candId is present),
             so its toast carries the same reversible Undo as the place-from-submission path. */
          showToast('Engagement created - ' + p.name + ' at ' + v.client, 'Undo', res.undo);
        }
      }}
    />
  );
}

function EngagementsScreen({ view = 'care' }) {
  const D = window.E8DATA;
  const { showToast, density } = React.useContext(E8Ctx);
  const isMobile = useIsMobile();
  const [careTab, setCareTab] = React.useState('all');
  const [insights, setInsights] = React.useState(D.insights.engagements);
  const counts = {
    all: D.engagements.length,
    overdue: D.engagements.filter((e) => e.care === 'overdue').length,
    due: D.engagements.filter((e) => e.care === 'due').length,
    current: D.engagements.filter((e) => e.care === 'current').length,
  };
  const rows = D.engagements.filter((e) => careTab === 'all' || e.care === careTab);
  const renewalRows = D.engagements.filter((e) => e.renewal !== 'Not due')
    .sort((a, b) => (Number(!!b.escalated) - Number(!!a.escalated)) || ((a.extensionPct || 0) - (b.extensionPct || 0)));

  /* Renewal pipeline analytics. */
  const Kpi = window.KpiCard;
  const parseK = (v) => parseFloat(String(v).replace(/[^0-9.]/g, '')) || 0;
  const totalK = renewalRows.reduce((n, e) => n + parseK(e.renewalValue), 0);
  const atRiskK = renewalRows.filter((e) => e.escalated || e.extensionPct < 50).reduce((n, e) => n + parseK(e.renewalValue), 0);
  const forecastK = Math.round(renewalRows.reduce((n, e) => n + parseK(e.renewalValue) * (e.extensionPct || 0) / 100, 0));
  const fmtMoney = (k) => (k >= 1000 ? '$' + (k / 1000).toFixed(2) + 'M' : '$' + Math.round(k) + 'K');
  const stageBuckets = D.renewalStages.map((s, i) => {
    const br = renewalRows.filter((e) => (e.renewalStage || 0) === i);
    return { label: s, count: br.length, value: br.reduce((n, e) => n + parseK(e.renewalValue), 0) };
  });

  /* Case studies - the proof stories behind engagements. */
  const csList = Object.keys(D.caseStudies || {}).map((id) => ({ id, ...D.caseStudies[id] }));
  const csPublished = csList.filter((c) => c.status === 'published').length;

  return (
    <React.Fragment>
      <Topbar
        crumbs={view === 'overview' ? [{ label: 'Engagements' }] : [{ label: 'Engagements', to: 'engagements' }, { label: view === 'renewals' ? 'Renewals' : view === 'casestudies' ? 'Case studies' : 'Care queue' }]}
        actions={
          <React.Fragment>
            <DSo.InsightsButton
              items={insights.map((i) => ({
                id: i.id,
                text: <span><b>{i.strong}</b>{i.rest}</span>,
                action: { label: i.action, onClick: () => showToast(i.id === 'i4' ? 'Escalated to Dana Whitfield (CSM)' : 'Care calls queued for 2 consultants', 'Undo') },
              }))}
              onDismiss={(id) => setInsights(insights.filter((i) => i.id !== id))}
            />
            <DSo.Button variant="secondary" size="sm" icon="upload">Export</DSo.Button>
            <DSo.Button variant="primary" size="sm" icon="add" onClick={() => window.e8OpenCreate && window.e8OpenCreate('engagement')}>New engagement</DSo.Button>
          </React.Fragment>
        }
      />
      <div className="e8-content">
        <div className="e8-page">
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, margin: '0 0 10px', flexWrap: 'wrap' }}>
            <DSo.SubTabs
              items={[
                { id: 'overview', label: 'Overview', count: (D.managedEngagements || []).length + D.engagements.filter((e) => !e.projectId).length },
                { id: 'care', label: 'Care queue', count: counts.overdue + counts.due },
                { id: 'renewals', label: 'Renewals', count: renewalRows.length },
                { id: 'casestudies', label: 'Case studies', count: csPublished },
              ]}
              active={view}
              onChange={(v) => navigate(v === 'renewals' ? 'renewals' : v === 'casestudies' ? 'casestudies' : v === 'care' ? 'care' : 'engagements')}
            />
            {view === 'care' ? (
              <DSo.SubTabs
                items={[
                  { id: 'all', label: 'All', count: counts.all },
                  { id: 'overdue', label: 'Overdue', count: counts.overdue },
                  { id: 'due', label: 'Due this week', count: counts.due },
                  { id: 'current', label: 'Current', count: counts.current },
                ]}
                active={careTab}
                onChange={setCareTab}
              />
            ) : view === 'overview' ? null : (
              <span style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)' }}>Everything ending or burning down in the next 90 days</span>
            )}
          </div>

          {view === 'overview' ? <EngagementsOverview /> : null}

          {view === 'renewals' ? (
            <React.Fragment>
              <div className="e8-kpi-row">
                {Kpi ? <Kpi label="Renewals in flight" value={String(renewalRows.length)} delta="next 90 days" dir="flat" spark={[2, 3, 3, 4, 4, renewalRows.length]} /> : null}
                {Kpi ? <Kpi label="Pipeline value" value={fmtMoney(totalK)} delta="annualized" dir="up" spark={[700, 820, 900, 1000, 1100, totalK]} /> : null}
                {Kpi ? <Kpi label="Likely to renew" value={fmtMoney(forecastK)} delta={(totalK ? Math.round((forecastK / totalK) * 100) : 0) + '% weighted'} dir="up" spark={[400, 500, 600, 650, 700, forecastK]} /> : null}
                {Kpi ? <Kpi label="At risk" value={fmtMoney(atRiskK)} delta="needs attention" dir="down" spark={[120, 160, 210, 260, 291, atRiskK]} /> : null}
              </div>
              <div className="e8-renew-stages">
                {stageBuckets.map((s, i) => (
                  <React.Fragment key={s.label}>
                    <div className={'e8-renew-stage' + (s.count ? ' is-active' : '')}>
                      <div className="e8-renew-stage-n tnum">{s.count}</div>
                      <div className="e8-renew-stage-label">{s.label}</div>
                      <div className="e8-renew-stage-val tnum">{s.value ? fmtMoney(s.value) : '-'}</div>
                    </div>
                    {i < stageBuckets.length - 1 ? <span className="material-symbols-outlined e8-renew-arrow">chevron_right</span> : null}
                  </React.Fragment>
                ))}
              </div>
            </React.Fragment>
          ) : null}

          {view === 'care' ? (isMobile ? (
            <div className="e8-cards">{rows.map((e) => <EngagementCareCard key={e.cid || e.consultant} e={e} />)}</div>
          ) : (
          <ConfigurableTable listKey="engagement-care"
            density={density}
            registry={[
              { key: 'consultant', label: 'Consultant', render: (e) => <NameCell name={e.consultant} sub={e.role} /> },
              { key: 'client', label: 'Client' },
              { key: 'po', label: 'PO health', width: 210, render: (e) => (
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                  <DSo.Badge tone={PO_HEALTH[e.po].tone} dot>{PO_HEALTH[e.po].label}</DSo.Badge>
                  <span style={{ width: 56, display: 'inline-flex' }}><DSo.Progress value={e.poBurn} soft={e.po === 'healthy'} /></span>
                  <span className="tnum" style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>{e.poBurn}%</span>
                </span>
              ) },
              { key: 'ends', label: 'Ends', num: true },
              { key: 'renewal', label: 'Renewal', render: (e) => (
                e.renewal === 'Not due'
                  ? <span style={{ color: 'var(--ui-text-tertiary)', fontSize: 'var(--ui-text-base)' }}>Not due</span>
                  : <DSo.Badge tone={e.renewal === 'Escalated' ? 'danger' : 'accent'}>{e.renewal}</DSo.Badge>
              ) },
              { key: 'care', label: 'Care touch', render: (e) => (
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                  <DSo.Badge tone={CARE[e.care].tone} dot>{CARE[e.care].label}</DSo.Badge>
                  <span className="tnum" style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)' }}>{e.lastTouch}</span>
                </span>
              ) },
            ]}
            rows={rows}
            rowActions={(e) => (
              <React.Fragment>
                <DSo.Button variant="ghost" size="sm" icon="call" onClick={() => showToast(`Care call queued for ${e.consultant}`, 'Undo')}>Log touch</DSo.Button>
                <DSo.Button variant="ghost" size="sm" icon="autorenew" onClick={() => showToast(`Renewal started for ${e.consultant}`)}>Renew</DSo.Button>
              </React.Fragment>
            )}
          />
          )) : view === 'renewals' ? (!renewalRows.length ? (
            <div style={{ maxWidth: 460, margin: '24px auto' }}><DSo.EmptyState icon="autorenew" title="No renewals in flight" body="Nothing is ending or burning down in the next 90 days. Extensions surface here as POs approach their limits." /></div>
          ) : isMobile ? (
            <div className="e8-cards">{renewalRows.map((e) => <EngagementRenewalCard key={e.cid} e={e} />)}</div>
          ) : (
          <ConfigurableTable listKey="engagement-renewals"
            density={density}
            onRowClick={(e) => navigate('consultant/' + e.cid)}
            registry={[
              { key: 'consultant', label: 'Consultant', render: (e) => <NameCell name={e.consultant} sub={e.role + ' · ' + e.client} /> },
              { key: 'value', label: 'Value', num: true, render: (e) => <span className="tnum" style={{ fontWeight: 500 }}>{e.renewalValue}</span> },
              { key: 'ends', label: 'Ends', num: true },
              { key: 'po', label: 'PO burn', width: 150, render: (e) => (
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                  <span style={{ width: 56, display: 'inline-flex' }}><DSo.Progress value={e.poBurn} soft={e.po === 'healthy'} /></span>
                  <span className="tnum" style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>{e.poBurn}%</span>
                </span>
              ) },
              { key: 'stage', label: 'Renewal stage', width: 220, render: (e) => (
                <DSo.StageCell stages={D.renewalStages} current={e.renewalStage || 0} />
              ) },
              { key: 'likelihood', label: 'Likelihood', width: 130, render: (e) => (
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                  <span style={{ width: 56, display: 'inline-flex' }}><DSo.Progress value={e.extensionPct} soft={e.extensionPct < 50} /></span>
                  <span className="tnum" style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>{e.extensionPct}%</span>
                </span>
              ) },
              { key: 'status', label: 'Status', render: (e) => (
                e.escalated
                  ? <DSo.Badge tone="danger" dot>Escalated - PO expired</DSo.Badge>
                  : <DSo.Badge tone={e.renewalStage > 0 ? 'info' : 'accent'} dot>{e.renewal}</DSo.Badge>
              ) },
              { key: 'sponsor', label: 'Sponsor', render: (e) => <ActorChip name={e.sponsor} /> },
            ]}
            rows={renewalRows}
            rowActions={(e) => (
              <React.Fragment>
                <DSo.Button variant="secondary" size="sm" icon="mail" onClick={() => showToast('Renewal email drafted to ' + e.sponsor + ' - review in Approvals', 'Open approvals', () => navigate('approvals'))}>Draft renewal email</DSo.Button>
                <DSo.MenuButton
                  items={[
                    { icon: 'description', label: 'View PO terms' },
                    { icon: 'trending_up', label: 'Propose rate change' },
                    { separator: true },
                    { icon: 'block', label: 'Mark non-renewing', danger: true },
                  ]}
                />
              </React.Fragment>
            )}
          />
          )) : null}
          {view === 'renewals' ? (
            <p style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)', margin: '10px 2px 0' }}>
              Renewal drafts are written from the engagement's history - rate, tenure, sentiment from care calls - and wait in Approvals.
            </p>
          ) : null}

          {view === 'casestudies' ? <CaseStudiesView /> : null}
        </div>
      </div>
    </React.Fragment>
  );
}

Object.assign(window, { VoiceOpsScreen, CallResultScreen, EngagementsScreen, NewEngagementDialog, CaseStudyCard, CaseStudiesView, OUTCOMES });
