// Shared: animated pipeline + YAML + workspace tree + merge queue.

const STAGES = [
  { id: 'triage',   name: 'Triage',     desc: 'classify, route, set scope' },
  { id: 'research', name: 'Research',   desc: 'read code, build context' },
  { id: 'plan',     name: 'Plan',       desc: 'propose approach' },
  { id: 'assess',   name: 'Assess',     desc: 'gate — manual or auto' },
  { id: 'execute',  name: 'Execute',    desc: 'write code, run tests' },
  { id: 'review',   name: 'Review',     desc: 'self-review diff' },
  { id: 'gap',      name: 'Gap Closure',desc: 'patch missed acceptance' },
  { id: 'pr',       name: 'Create PR',  desc: 'open, label, request review' },
];

// Live-looking pipeline that ticks through stages forever.
function LivePipeline({ compact = false }) {
  const [active, setActive] = React.useState(2);
  const [tick, setTick] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => {
      setTick((t) => t + 1);
      setActive((a) => (a + 1) % STAGES.length);
    }, 1800);
    return () => clearInterval(id);
  }, []);
  const stageState = (i) => {
    if (i < active) return 'done';
    if (i === active) return 'live';
    return 'pending';
  };
  return (
    <div className={`pipeline ${compact ? 'pipeline-compact' : ''}`}>
      <div className="pipeline-head">
        <div className="pipeline-meta">
          <span className="pipeline-id">FEAT-2247</span>
          <span className="pipeline-title">Add OAuth fallback for self-hosted SSO</span>
        </div>
        <div className="pipeline-status">
          <span className="status-dot" />
          <span className="status-label">running</span>
          <span className="status-elapsed">00:{String(12 + (tick % 48)).padStart(2,'0')}:34</span>
        </div>
      </div>
      <div className="pipeline-track">
        {STAGES.map((s, i) => (
          <div key={s.id} className={`stage stage-${stageState(i)}`}>
            <div className="stage-bar"><div className="stage-fill" /></div>
            <div className="stage-name">{s.name}</div>
            <div className="stage-desc">{s.desc}</div>
          </div>
        ))}
      </div>
      <div className="pipeline-foot">
        <div className="pipeline-log">
          <LogStream tick={tick} active={active} />
        </div>
      </div>
    </div>
  );
}

const LOG_LINES = [
  ['research', 'reading src/auth/sso.ts ... 412 lines'],
  ['research', 'reading src/auth/oauth.ts ... 287 lines'],
  ['research', 'grep "fallback" → 14 hits across 6 files'],
  ['plan',     'wrote 3 plan artifacts — provider config, token exchange, refresh loop'],
  ['expand',   'branch feat/oauth-fallback-2247 created · 3 execute stages spawned'],
  ['execute',  'execute-plan(token-exchange): wrote src/auth/oauth-fallback.ts (+148 −0)'],
  ['execute',  'execute-plan(provider-config): pnpm test — 73 pass, 0 fail in 4.2s'],
  ['review',   'diff review: no secrets, acceptance criteria covered'],
  ['create-pr','opened PR #1142 → main · ready for review'],
];
function LogStream({ tick }) {
  const visible = 4;
  const start = tick % LOG_LINES.length;
  const rows = Array.from({ length: visible }, (_, i) => LOG_LINES[(start + i) % LOG_LINES.length]);
  return (
    <div className="logstream">
      {rows.map(([k, v], i) => (
        <div key={`${tick}-${i}`} className="logstream-row" style={{ opacity: 1 - i * 0.18 }}>
          <span className="logstream-key">[{k}]</span>
          <span className="logstream-val">{v}</span>
        </div>
      ))}
    </div>
  );
}

// ── YAML pipeline-as-code ──────────────────────────────────
// Summarized view of workspace-task-execution.yaml — the workflow that
// runs against an approved Task. Real fields, fewer of them.
const YAML_BLOCKS = [
  { id: 'meta', lines: [['k', 'name'], ['p', ': '], ['s', '"Workspace Task Execution"']] },
  { id: 'meta', lines: [['k', 'triggers'], ['p', ':']] },
  { id: 'meta', lines: [['p', '  - '], ['k', 'type'], ['p', ': '], ['s', 'manual']] },
  { id: 'sep', lines: [['c', '# stages run sequentially; expand fans execute-plan out in parallel']] },
  { id: 'research', heading: true, lines: [['k', 'stages'], ['p', ':']] },
  { id: 'research', lines: [['p', '  - '], ['k', 'kind'], ['p', ': '], ['s', 'stage']] },
  { id: 'research', lines: [['p', '    '], ['k', 'name'], ['p', ': '], ['s', 'research']] },
  { id: 'research', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'ai'], ['p', '   '], ['k', 'timeout'], ['p', ': '], ['s', '15m']] },
  { id: 'plan', lines: [['p', '  - '], ['k', 'name'], ['p', ': '], ['s', 'plan']] },
  { id: 'plan', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'ai'], ['p', '   '], ['k', 'timeout'], ['p', ': '], ['s', '20m']] },
  { id: 'check', lines: [['p', '  - '], ['k', 'name'], ['p', ': '], ['s', 'check-plan-status']] },
  { id: 'check', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'api']] },
  { id: 'expand', lines: [['p', '  - '], ['k', 'name'], ['p', ': '], ['s', 'expand']] },
  { id: 'expand', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'api'], ['p', '  '], ['k', 'creates'], ['p', ': '], ['s', 'branch']] },
  { id: 'execute', lines: [['p', '  - '], ['k', 'kind'], ['p', ': '], ['s', 'dynamic_stage']] },
  { id: 'execute', lines: [['p', '    '], ['k', 'name'], ['p', ': '], ['s', 'execute-plan']] },
  { id: 'execute', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'ai'], ['p', '   '], ['k', 'parallel'], ['p', ': '], ['s', 'true']] },
  { id: 'review', lines: [['p', '  - '], ['k', 'name'], ['p', ': '], ['s', 'review']] },
  { id: 'review', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'ai'], ['p', '   '], ['k', 'timeout'], ['p', ': '], ['s', '10m']] },
  { id: 'pr', lines: [['p', '  - '], ['k', 'name'], ['p', ': '], ['s', 'create-pr']] },
  { id: 'pr', lines: [['p', '    '], ['k', 'runs_on'], ['p', ': '], ['s', 'api']] },
];
const YAML_NOTES = {
  research: ['Research', 'AI reads the repo with Read / Glob / Grep, gathers context, saves findings as a workflow artifact.'],
  plan:     ['Plan', 'AI breaks the work into independent plan artifacts — one per focused unit. Markdown you can read and edit.'],
  check:    ['Check Plan Status', 'Verifies plan stage produced valid artifacts. If it returned a blocker instead, the run halts here.'],
  expand:   ['Expand', 'Creates the work-item branch, pushes it, and fans the plan artifacts out into one execute-plan stage each.'],
  execute:  ['Execute Plan', 'Dynamic — one stage per plan, run in parallel. AI writes code on the branch, runs build / tests, commits.'],
  review:   ['Review', 'AI reviews the full diff for regressions, secrets, and acceptance-criteria gaps before the PR opens.'],
  pr:       ['Create PR', 'Opens the pull request against the base branch. Real Git, real CI.'],
};
function YamlBlock() {
  const [hover, setHover] = React.useState('execute');
  const note = YAML_NOTES[hover] || YAML_NOTES.execute;
  return (
    <div className="yaml-wrap">
      <div className="yaml-frame">
        <div className="yaml-frame-head">
          <span className="yaml-dot" /><span className="yaml-dot" /><span className="yaml-dot" />
          <span className="yaml-frame-name">workflows/workspace-task-execution.yaml</span>
        </div>
        <pre className="yaml-pre">
{YAML_BLOCKS.map((blk, i) => (
  <div key={i} className={`yaml-line ${hover === blk.id ? 'yaml-line-hot' : ''}`}
       onMouseEnter={() => YAML_NOTES[blk.id] && setHover(blk.id)}>
    {blk.lines.map((tok, j) => (
      <span key={j} className={`yaml-tok yaml-${tok[0]}`}>{tok[1]}</span>
    ))}
  </div>
))}
        </pre>
      </div>
      <div className="yaml-note">
        <div className="yaml-note-eyebrow">stage</div>
        <div className="yaml-note-title">{note[0]}</div>
        <div className="yaml-note-body">{note[1]}</div>
        <div className="yaml-note-hint">hover any stage in the YAML</div>
      </div>
    </div>
  );
}

// ── Material-style icons (inline SVG) ──────────────────────
// Default tints follow the real product: Feature=amber/orange, Task=green,
// Bug=red, Folder=blue, Doc=gray.
const ICON_TINT = {
  Extension:  '#f59e0b',  // Feature — amber jigsaw
  LocalOffer: '#66bb6a',  // Task — green tag
  BugReport:  '#cc6666',  // Bug — muted red
  Folder:     '#42a5f5',  // Folder — blue
  Description:'#9e9e9e',  // Doc — gray
};
function MIcon({ name, size = 18, color }) {
  // Simplified Material glyphs
  const paths = {
    Extension: 'M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7s2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z',
    BugReport: 'M20 8h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z',
    LocalOffer: 'M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z',
    Folder: 'M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z',
    Description: 'M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z',
    Sync: 'M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z',
    HourglassEmpty: 'M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6z',
    CheckCircle: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z',
    MergeType: 'M17 20.41L18.41 19 15 15.59 13.59 17 17 20.41zM7.5 8H11v5.59L5.59 19 7 20.41l6-6V8h3.5L12 3.5 7.5 8z',
    RateReview: 'M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 14H6v-2h7v2zm5-4H6v-2h12v2zm0-4H6V6h12v2z',
    Cancel: 'M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z',
    Error: 'M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z',
    LockOpen: 'M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1s3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z',
  };
  const d = paths[name] || paths.Description;
  const fill = color || ICON_TINT[name] || 'currentColor';
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} style={{ flexShrink: 0 }}>
      <path d={d} />
    </svg>
  );
}

// Status overlay for tree icons
function StatusOverlay({ kind }) {
  // Status pip in bottom-right of the entity icon (matches product UI).
  const map = {
    'pulse-amber':  { color: '#e89c30', dot: true, pulse: true },
    'dot-blue':     { color: '#42a5f5', dot: true },
    'dot-purple':   { color: '#ab47bc', dot: true },
    'check-green':  { color: '#66bb6a', icon: 'check' },
    'x-gray':       { color: '#9e9e9e', icon: 'x' },
    'error-red':    { color: '#ef5350', icon: '!' },
  };
  const s = map[kind];
  if (!s) return null;
  return (
    <span className={`tree-overlay ${s.pulse ? 'tree-overlay-pulse' : ''}`} style={{ background: s.color }}>
      {s.icon === 'check' && <svg width="7" height="7" viewBox="0 0 24 24" fill="#0c0d0a"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>}
      {s.icon === 'x' && <svg width="7" height="7" viewBox="0 0 24 24" fill="#0c0d0a"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>}
      {s.icon === '!' && <span style={{ color: '#0c0d0a', fontSize: 8, fontWeight: 800, lineHeight: 1 }}>!</span>}
    </span>
  );
}

// Workspace tree — Material icons + status overlays.
function WorkspaceTree() {
  const rows = [
    { kind: 'Extension', id: 'FEAT-001', title: 'OAuth fallback for self-hosted SSO',  overlay: 'pulse-amber', questions: 2 },
    { kind: 'LocalOffer',id: 'TASK-002', title: 'Add provider config schema',           overlay: 'check-green', child: true },
    { kind: 'LocalOffer',id: 'TASK-003', title: 'Implement token exchange',             overlay: 'dot-blue',    child: true },
    { kind: 'BugReport', id: 'BUG-004',  title: 'Refresh-token loop on 401',            overlay: 'pulse-amber', child: true },
    { kind: 'Extension', id: 'FEAT-005', title: 'Multi-repo workspace switcher',        overlay: 'dot-purple' },
    { kind: 'Folder',    id: 'FLDR-006', title: 'Auth & SSO',                            overlay: null,          folderOpen: true },
    { kind: 'Extension', id: 'FEAT-007', title: 'Per-pipeline cost ceilings',           overlay: 'check-green', child: true },
    { kind: 'BugReport', id: 'BUG-008',  title: 'SignalR reconnect storm under load',   overlay: 'error-red',   child: true },
  ];
  return (
    <div className="wstree">
      <div className="wstree-head">Workspace</div>
      {rows.map((r, i) => (
        <div key={i} className={`wstree-row ${r.child ? 'wstree-child' : ''} ${r.folderOpen ? 'wstree-folder-open' : ''}`}>
          <span className="wstree-icon">
            <MIcon name={r.kind} size={20} />
            <StatusOverlay kind={r.overlay} />
            {r.questions ? <span className="tree-q">{r.questions}</span> : null}
          </span>
          <span className="wstree-id">{r.id}</span>
          <span className="wstree-title">{r.title}</span>
        </div>
      ))}
    </div>
  );
}

// ── Automatic Merge Queue ──────────────────────────────────
function MergeQueue() {
  const [tick, setTick] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setTick((t) => t + 1), 2200);
    return () => clearInterval(id);
  }, []);

  // Cycle the head item through: Rebasing → WaitingChecks → Merging → Merged
  const headStates = ['Rebasing', 'WaitingChecks', 'Merging', 'Merged'];
  const headState = headStates[tick % headStates.length];

  const STATE_META = {
    Queued:           { icon: 'HourglassEmpty', color: '#42a5f5', label: 'Queued' },
    Rebasing:         { icon: 'Sync',           color: '#e89c30', label: 'Rebasing',  spin: true },
    WaitingChecks:    { icon: 'HourglassEmpty', color: '#e89c30', label: 'Waiting CI' },
    Merging:          { icon: 'Sync',           color: '#42a5f5', label: 'Merging',   spin: true },
    Merged:           { icon: 'CheckCircle',    color: '#66bb6a', label: 'Merged' },
    Conflict:         { icon: 'MergeType',      color: '#e89c30', label: 'Conflict' },
    NeedsApproval:    { icon: 'RateReview',     color: '#e89c30', label: 'Needs review' },
    Broken:           { icon: 'Error',          color: '#ef5350', label: 'Broken' },
  };

  const items = [
    { num: 1142, kind: 'Extension', title: 'Add OAuth fallback for self-hosted SSO',     author: 'sourceweaver-bot', state: headState, head: true },
    { num: 1141, kind: 'Extension', title: 'Per-pipeline cost ceilings',                 author: 'sourceweaver-bot', state: 'Queued' },
    { num: 1140, kind: 'BugReport', title: 'Refresh-token loop on 401',                  author: 'sourceweaver-bot', state: 'Conflict', recovery: 'conflict' },
    { num: 1139, kind: 'Extension', title: 'Multi-repo workspace switcher',              author: 'sourceweaver-bot', state: 'Broken',  recovery: 'fix' },
    { num: 1138, kind: 'BugReport', title: 'SignalR reconnect storm under load',         author: 'sourceweaver-bot', state: 'Queued' },
  ];

  return (
    <div className="mqueue">
      <div className="mqueue-head">
        <div>
          <div className="mqueue-eyebrow">merge queue · main</div>
          <div className="mqueue-title">5 PRs queued · self-healing</div>
        </div>
        <div className="mqueue-status">
          <span className="status-dot" />
          <span className="status-label">live</span>
        </div>
      </div>
      <div className="mqueue-list">
        {items.map((it, i) => {
          const s = STATE_META[it.state];
          return (
            <React.Fragment key={it.num}>
              <div className={`mqueue-row ${it.head ? 'is-head' : ''}`}>
                <span className="mqueue-pos">{i + 1}</span>
                <span className="mqueue-pr-icon"><MIcon name={it.kind} size={16} /></span>
                <span className="mqueue-pr">#{it.num}</span>
                <span className="mqueue-pr-title">{it.title}</span>
                <span className="mqueue-pr-author">{it.author}</span>
                <span className="mqueue-state" style={{ color: s.color, borderColor: s.color }}>
                  <span className={s.spin ? 'mqueue-spin' : ''}><MIcon name={s.icon} size={14} color={s.color} /></span>
                  <span>{s.label}</span>
                </span>
              </div>
              {it.recovery === 'conflict' && (
                <div className="mqueue-recovery">
                  <span className="mqueue-recovery-arrow">↳</span>
                  <span className="mqueue-recovery-tag">conflict-resolution.yaml</span>
                  <span className="mqueue-recovery-text">cloning · squashing · resolving with Claude Code · force-push-with-lease</span>
                </div>
              )}
              {it.recovery === 'fix' && (
                <div className="mqueue-recovery">
                  <span className="mqueue-recovery-arrow">↳</span>
                  <span className="mqueue-recovery-tag">merge-queue-fix.yaml</span>
                  <span className="mqueue-recovery-text">collecting CI logs · diagnosing root cause · patching branch · retrying</span>
                </div>
              )}
            </React.Fragment>
          );
        })}
      </div>
      <div className="mqueue-foot">
        <div className="mqueue-recovery-card">
          <div className="mqueue-recovery-name">on_pr_conflict</div>
          <div className="mqueue-recovery-desc">Rebase hits a conflict → squash, rebase, resolve with Claude Code, force-push.</div>
        </div>
        <div className="mqueue-recovery-card">
          <div className="mqueue-recovery-name">on_merge_queue_broken</div>
          <div className="mqueue-recovery-desc">CI red or build break → collect failure context, diagnose, fix, retry.</div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { LivePipeline, YamlBlock, WorkspaceTree, MergeQueue, MIcon, STAGES });
