// Timeline (Gantt-style swimlanes) + Activity feed views

/* ---------------- Timeline ---------------- */

// Window: -75min … +25min relative to "now". 100 units wide, now at 75%.
// left% = minutesRelativeToNow + 75 ; width% = durationMinutes
const TL_WINDOW = 100;
const TL_NOW = 75;

// Deterministic timing per task id, in minutes relative to now (neg = past).
const TL_TIMING = {
  // installer-agent
  'TSK-3964': { start: -68, end: -54 },                 // done · Install Auth
  'TSK-3965': { start: -50, end: -40 },                 // done · Bump Next
  'TSK-3954': { start: -32, end: -20, halt: true },     // failed · Analytics
  'TSK-3999': { start: -14, end: 22 },                  // running 28%
  'TSK-4006': { start: 2,   end: 14 },                  // ready
  'TSK-4012': { start: 16,  end: 24 },                  // backlog
  // tester-agent
  'TSK-4000': { start: -22, end: 4 },                   // running 81%
  'TSK-4008': { start: 6,   end: 20 },                  // ready
  // migrator-agent
  'TSK-3998': { start: -28, end: 12 },                  // running 62%
  'TSK-4015': { start: 14,  end: 24 },                  // backlog
  // reviewer-agent
  'TSK-3987': { start: -45, end: -29, await: true },    // review
  'TSK-3988': { start: -27, end: -15, await: true },    // review
  // sentinel-agent
  'TSK-3966': { start: -67, end: -53 },                 // done · autoscaler
  'TSK-4007': { start: 1,   end: 9 },                   // ready
  'TSK-4013': { start: 11,  end: 21 },                  // backlog
};

const TL_LANES = [
  { agent: 'installer-agent', kind: 'installer' },
  { agent: 'migrator-agent',  kind: 'migrator' },
  { agent: 'tester-agent',    kind: 'tester' },
  { agent: 'reviewer-agent',  kind: 'reviewer' },
  { agent: 'sentinel-agent',  kind: 'sentinel' },
];

const TL_TICKS = [-75, -60, -45, -30, -15, 0, 15, 25];
function tickLabel(m) {
  if (m === 0) return 'now';
  return (m > 0 ? '+' : '') + m + 'm';
}

function TimelineBar({ task, project, timing, onSelect, selected }) {
  const left = timing.start + 75;
  const width = Math.max(2, timing.end - timing.start);
  const status = task.column;

  // For running tasks, split into elapsed (solid) and projected (ghost)
  const isRunning = status === 'running';
  const elapsedW = isRunning ? Math.max(0, 0 - timing.start) : width;
  const elapsedPct = (elapsedW / width) * 100;

  return (
    <button
      type="button"
      className={`tl-bar status-${status} ${selected ? 'sel' : ''} ${timing.halt ? 'halt' : ''}`}
      style={{ left: `${left}%`, width: `${width}%` }}
      onClick={() => onSelect(task.id)}
      title={`${task.id} · ${task.title}`}
    >
      {isRunning && (
        <span className="tl-bar-fill" style={{ width: `${elapsedPct}%` }} />
      )}
      <span className="tl-bar-label">
        <span className="tl-bar-swatch" style={{ background: project.color }} />
        <span className="tl-bar-id">{task.id}</span>
        <span className="tl-bar-title">{task.title}</span>
        {isRunning && <span className="tl-bar-pct">{Math.round(task.progress)}%</span>}
        {timing.await && <span className="tl-bar-flag review">awaiting approval</span>}
        {timing.halt && <span className="tl-bar-flag failed">halted</span>}
      </span>
    </button>
  );
}

function TimelineView({ tasks, onSelect, selectedId }) {
  const { PROJECTS } = window.ORC_DATA;
  const projectsById = Object.fromEntries(PROJECTS.map(p => [p.id, p]));
  const tasksById = Object.fromEntries(tasks.map(t => [t.id, t]));

  const counts = {
    running: tasks.filter(t => t.column === 'running').length,
    review:  tasks.filter(t => t.column === 'review').length,
    queued:  tasks.filter(t => t.column === 'ready' || t.column === 'backlog').length,
  };

  return (
    <div className="tl-wrap">
      <div className="tl-legend">
        <span className="tl-legend-title">Execution timeline</span>
        <span className="tl-legend-sub">window −75m … +25m · projected work shown ghosted</span>
        <span className="tl-legend-spacer" />
        {[
          { k: 'running', label: 'running' },
          { k: 'review', label: 'review' },
          { k: 'done', label: 'done' },
          { k: 'failed', label: 'failed' },
          { k: 'ready', label: 'queued' },
        ].map(s => (
          <span key={s.k} className="tl-legend-item">
            <span className={`tl-legend-dot s-${s.k}`} />{s.label}
          </span>
        ))}
      </div>

      <div className="tl-grid">
        {/* Axis header */}
        <div className="tl-axis-corner" />
        <div className="tl-axis">
          {TL_TICKS.map(m => (
            <span
              key={m}
              className={`tl-tick ${m === 0 ? 'now' : ''}`}
              style={{ left: `${m + 75}%` }}
            >
              {tickLabel(m)}
            </span>
          ))}
        </div>

        {/* Lanes */}
        {TL_LANES.map(lane => {
          const laneTasks = tasks
            .filter(t => t.agent === lane.agent && TL_TIMING[t.id])
            .sort((a, b) => TL_TIMING[a.id].start - TL_TIMING[b.id].start);
          return (
            <React.Fragment key={lane.agent}>
              <div className="tl-lane-head">
                <span className="tl-lane-avatar">{lane.kind.slice(0, 2).toUpperCase()}</span>
                <span className="tl-lane-name">{lane.agent}</span>
                <span className="tl-lane-count">{laneTasks.length}</span>
              </div>
              <div className="tl-lane">
                {/* gridlines */}
                {TL_TICKS.map(m => (
                  <span key={m} className={`tl-gridline ${m === 0 ? 'now' : ''}`} style={{ left: `${m + 75}%` }} />
                ))}
                {laneTasks.map(t => (
                  <TimelineBar
                    key={t.id}
                    task={t}
                    project={projectsById[t.project]}
                    timing={TL_TIMING[t.id]}
                    onSelect={onSelect}
                    selected={selectedId === t.id}
                  />
                ))}
              </div>
            </React.Fragment>
          );
        })}

        {/* now line spanning lanes */}
        <div className="tl-now-line" style={{ left: `calc(var(--tl-gutter) + ${TL_NOW}% * (100% - var(--tl-gutter)) / 100)` }} />
      </div>

      <div className="tl-foot">
        <span className="chip chip-mono"><span className="dot pulse" style={{ color: 'var(--c-status-running)' }} />{counts.running} running</span>
        <span className="chip chip-mono"><span className="dot" style={{ color: 'var(--c-status-review)' }} />{counts.review} in review</span>
        <span className="chip chip-mono"><span className="dot" style={{ color: 'var(--c-text-3)' }} />{counts.queued} queued</span>
      </div>
    </div>
  );
}

/* ---------------- Activity ---------------- */

const ACTIVITY_FULL = [
  { id: 'a1', type: 'install', agent: 'installer-agent', project: 'zeny',
    title: 'Installed auth@2.4.0', detail: 'Applied to prod-us · 4 services restarted · 0 errors',
    ago: '12s ago', time: '00:14:22', task: 'TSK-3964' },
  { id: 'a2', type: 'migrate', agent: 'migrator-agent', project: 'tlsdeck',
    title: 'Partition rollout on users table', detail: '7 / 12 shards backfilled · shadow read parity 100%',
    ago: '38s ago', time: '00:13:08', task: 'TSK-3998' },
  { id: 'a3', type: 'review', agent: 'reviewer-agent', project: 'vidix',
    title: 'Requested approval — Stripe v3 webhooks', detail: '+412 / −188 across 6 files · affects payments',
    ago: '1m ago', time: '00:11:51', task: 'TSK-3987' },
  { id: 'a4', type: 'fail', agent: 'installer-agent', project: 'worldtime',
    title: 'Halted analytics@0.4.0 install', detail: 'dependency conflict: pg-extensions/citext@1.6.3 missing',
    ago: '4m ago', time: '00:09:42', task: 'TSK-3954' },
  { id: 'a5', type: 'test', agent: 'tester-agent', project: 'zeny',
    title: 'Dispatched regression suite', detail: '218 assertions queued on staging.zeny',
    ago: '6m ago', time: '00:07:30', task: 'TSK-4008' },
  { id: 'a6', type: 'rotate', agent: 'sentinel-agent', project: 'tlsdeck',
    title: 'Rotated KMS signing keys', detail: 'us-east-2 · propagated to 14 services',
    ago: '9m ago', time: '00:04:12', task: 'TSK-4007' },
  { id: 'a7', type: 'update', agent: 'installer-agent', project: 'zeny',
    title: 'Applied next@15.3 runtime bump', detail: 'across 3 projects · cold-start −8%',
    ago: '16m ago', time: '23:58:01', task: 'TSK-3965' },
  { id: 'a8', type: 'install', agent: 'installer-agent', project: 'worldtime',
    title: 'Started blog@0.8.4 install', detail: 'building bundle · 28% · est. 1m 40s',
    ago: '18m ago', time: '23:55:18', task: 'TSK-3999' },
  { id: 'a9', type: 'review', agent: 'reviewer-agent', project: 'tlsdeck',
    title: 'Requested approval — backfill org_id', detail: '1 migration · affects DB · 41k invoice rows',
    ago: '24m ago', time: '23:49:30', task: 'TSK-3988' },
  { id: 'a10', type: 'install', agent: 'sentinel-agent', project: 'vidix',
    title: 'Deployed autoscaler config', detail: 'prod-us · min 4 / max 32 replicas',
    ago: '31m ago', time: '23:42:55', task: 'TSK-3966' },
  { id: 'a11', type: 'test', agent: 'tester-agent', project: 'vidix',
    title: 'Rebuilt knowledge embeddings', detail: '81% · 6,402 docs re-indexed',
    ago: '40m ago', time: '23:33:12', task: 'TSK-4000' },
];

const ACT_TYPES = {
  install: { icon: 'box',    color: 'var(--c-status-done)',    label: 'Install' },
  update:  { icon: 'arrowRight', color: 'var(--c-status-running)', label: 'Update' },
  migrate: { icon: 'database', color: 'var(--c-status-running)', label: 'Migration' },
  review:  { icon: 'shield',  color: 'var(--c-status-review)',  label: 'Approval' },
  fail:    { icon: 'alert',   color: 'var(--c-status-failed)',  label: 'Failure' },
  test:    { icon: 'zap',     color: 'var(--c-status-running)', label: 'Test' },
  rotate:  { icon: 'refresh', color: 'var(--c-status-done)',    label: 'Security' },
};

const ACT_FILTERS = [
  { id: 'all',    label: 'All' },
  { id: 'install', label: 'Installs & updates', types: ['install', 'update', 'migrate'] },
  { id: 'review', label: 'Approvals', types: ['review'] },
  { id: 'fail',   label: 'Failures', types: ['fail'] },
  { id: 'test',   label: 'Tests', types: ['test'] },
];

function ActivityView({ onSelect }) {
  const { PROJECTS } = window.ORC_DATA;
  const projectsById = Object.fromEntries(PROJECTS.map(p => [p.id, p]));
  const [filter, setFilter] = React.useState('all');

  const def = ACT_FILTERS.find(f => f.id === filter);
  const items = filter === 'all'
    ? ACTIVITY_FULL
    : ACTIVITY_FULL.filter(a => def.types.includes(a.type));

  // bucket by recency
  const recent = items.filter(a => a.ago.includes('s ago') || a.ago === '1m ago');
  const earlier = items.filter(a => !(a.ago.includes('s ago') || a.ago === '1m ago'));

  const renderRow = (a) => {
    const t = ACT_TYPES[a.type];
    const project = projectsById[a.project];
    return (
      <button type="button" className="act-row" key={a.id} onClick={() => onSelect && onSelect(a.task)}>
        <span className="act-rail">
          <span className="act-icon" style={{ color: t.color, borderColor: `color-mix(in oklch, ${t.color} 30%, var(--c-line))`, background: `color-mix(in oklch, ${t.color} 10%, var(--c-surface))` }}>
            <Icon name={t.icon} size={13} />
          </span>
        </span>
        <span className="act-main">
          <span className="act-line1">
            <span className="act-title">{a.title}</span>
            <span className={`act-type-pill`} style={{ color: t.color }}>{t.label}</span>
          </span>
          <span className="act-detail">{a.detail}</span>
          <span className="act-line3">
            <span className="act-meta"><Icon name="cpu" size={10} />{a.agent}</span>
            <span className="act-meta"><span className="swatch" style={{ background: project.color, width: 8, height: 8, borderRadius: 2 }} />{project.name}</span>
            <span className="act-meta act-task">{a.task}</span>
          </span>
        </span>
        <span className="act-time">
          <span className="act-ago">{a.ago}</span>
          <span className="act-abs">{a.time} UTC</span>
        </span>
      </button>
    );
  };

  return (
    <div className="act-wrap">
      <div className="act-filters">
        {ACT_FILTERS.map(f => (
          <button
            key={f.id}
            type="button"
            className={`tab ${filter === f.id ? 'active' : ''}`}
            onClick={() => setFilter(f.id)}
          >
            {f.label}
            <span className="count">
              {f.id === 'all' ? ACTIVITY_FULL.length : ACTIVITY_FULL.filter(a => f.types.includes(a.type)).length}
            </span>
          </button>
        ))}
        <span className="act-live">
          <span className="dot pulse" style={{ color: 'var(--c-status-done)' }} />
          live stream
        </span>
      </div>

      <div className="act-feed">
        {recent.length > 0 && <div className="act-bucket">Just now</div>}
        {recent.map(renderRow)}
        {earlier.length > 0 && <div className="act-bucket">Earlier</div>}
        {earlier.map(renderRow)}
        {items.length === 0 && <div className="empty" style={{ margin: 20 }}>no events match this filter</div>}
      </div>
    </div>
  );
}

window.TimelineView = TimelineView;
window.ActivityView = ActivityView;
