/**
 * ProgressTracker.jsx
 * Owns: rendering multi-step agent progress with status icons, durations, ETA, and
 *       connector lines. Compact (horizontal pill row) and expanded (vertical timeline) modes.
 * Does NOT own: routing, session state, or step orchestration.
 *
 * AG-UI events consumed (via ProgressTrackerContainer):
 *   STEP_STARTED    { detail: { stepId, label, description? } }
 *   STEP_COMPLETED  { detail: { stepId } }
 *   STEP_FAILED     { detail: { stepId, error? } }
 *   STEP_SKIPPED    { detail: { stepId } }
 *   PROGRESS_UPDATE { detail: { stepId, description? } }
 *
 * Step shape:
 *   { id, label, description?, status: 'pending'|'running'|'done'|'failed'|'skipped',
 *     startedAt?, completedAt?, error? }
 *
 * Props (ProgressTracker — controlled):
 *   steps: Step[]
 *   currentStepId?: string
 *   variant?: 'compact'|'expanded'  (default: 'expanded')
 *   theme?: 'light'|'dark'|'auto'
 *   showETA?: boolean
 *
 * Zero dependencies beyond React.
 */

const { useState, useEffect, useRef, useCallback } = React;

// ─── Design tokens (matches ToolCallCard / TokenMeter language) ──────────────
const TOKENS = {
  light: {
    bg: '#FAFAF8',
    card: '#FFFFFF',
    border: '#E8E6E0',
    fg: '#1a1a1a',
    fgMuted: '#888',
    accent: '#F5A623',
    success: '#22c55e',
    successBg: 'rgba(34,197,94,0.1)',
    error: '#ef4444',
    errorBg: 'rgba(239,68,68,0.08)',
    running: '#3b82f6',
    runningBg: 'rgba(59,130,246,0.1)',
    pending: '#94a3b8',
    pendingBg: 'rgba(148,163,184,0.1)',
    skipped: '#94a3b8',
    skippedBg: 'rgba(148,163,184,0.07)',
    connector: '#E8E6E0',
    shadow: '0 2px 14px rgba(0,0,0,0.07)',
    tooltipBg: '#1C1C1A',
    tooltipFg: '#F0EEE8',
  },
  dark: {
    bg: '#111110',
    card: '#1C1C1A',
    border: '#2E2E2C',
    fg: '#F0EEE8',
    fgMuted: '#666',
    accent: '#F5A623',
    success: '#4ade80',
    successBg: 'rgba(74,222,128,0.1)',
    error: '#f87171',
    errorBg: 'rgba(248,113,113,0.08)',
    running: '#60a5fa',
    runningBg: 'rgba(96,165,250,0.1)',
    pending: '#475569',
    pendingBg: 'rgba(71,85,105,0.15)',
    skipped: '#475569',
    skippedBg: 'rgba(71,85,105,0.1)',
    connector: '#2E2E2C',
    shadow: '0 2px 14px rgba(0,0,0,0.5)',
    tooltipBg: '#2E2E2C',
    tooltipFg: '#F0EEE8',
  },
};

function resolveTheme(theme) {
  if (theme !== 'auto') return theme || 'light';
  if (typeof window === 'undefined') return 'light';
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

// CSS keyframes — injected once
const PT_STYLES = `
@keyframes pt-spin   { to { transform: rotate(360deg); } }
@keyframes pt-pulse  { 0%,100%{opacity:1} 50%{opacity:0.5} }
@keyframes pt-fade-in{ from{opacity:0;transform:translateY(4px)} to{opacity:1;transform:translateY(0)} }
@keyframes pt-pop    { 0%{transform:scale(0.7);opacity:0} 70%{transform:scale(1.12)} 100%{transform:scale(1);opacity:1} }
`;

let _ptStylesInjected = false;
function injectPTStyles() {
  if (_ptStylesInjected || typeof document === 'undefined') return;
  const el = document.createElement('style');
  el.textContent = PT_STYLES;
  document.head.appendChild(el);
  _ptStylesInjected = true;
}

// ─── Duration helpers ─────────────────────────────────────────────────────────

function formatDuration(ms) {
  if (ms == null || ms < 0) return '';
  if (ms < 1000) return `${Math.round(ms)}ms`;
  if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
  const m = Math.floor(ms / 60000);
  const s = Math.round((ms % 60000) / 1000);
  return `${m}m ${s}s`;
}

// Median of an array
function median(arr) {
  if (!arr.length) return null;
  const sorted = [...arr].sort((a, b) => a - b);
  const mid = Math.floor(sorted.length / 2);
  return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
}

// Estimate remaining time: median of completed step durations × remaining step count
function calcETA(steps) {
  const durations = steps
    .filter(s => s.status === 'done' && s.startedAt && s.completedAt)
    .map(s => s.completedAt - s.startedAt);
  const med = median(durations);
  if (med == null) return null;
  const remaining = steps.filter(s => s.status === 'pending' || s.status === 'running').length;
  if (remaining === 0) return 0;
  return med * remaining;
}

// ─── Status icons ─────────────────────────────────────────────────────────────

function SpinnerIcon({ color, size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none"
      style={{ animation: 'pt-spin 0.7s linear infinite', flexShrink: 0 }}>
      <circle cx="8" cy="8" r="6" stroke={color} strokeWidth="2" strokeOpacity="0.25" />
      <path d="M8 2a6 6 0 0 1 6 6" stroke={color} strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

function CheckIcon({ color, size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none"
      style={{ animation: 'pt-pop 0.25s ease', flexShrink: 0 }}>
      <circle cx="8" cy="8" r="7" fill={color} fillOpacity="0.15" />
      <path d="M4.5 8.5l2.5 2.5 4.5-5" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function XIcon({ color, size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none"
      style={{ animation: 'pt-pop 0.2s ease', flexShrink: 0 }}>
      <circle cx="8" cy="8" r="7" fill={color} fillOpacity="0.12" />
      <path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke={color} strokeWidth="1.8" strokeLinecap="round" />
    </svg>
  );
}

function SkipIcon({ color, size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none" style={{ flexShrink: 0 }}>
      <circle cx="8" cy="8" r="7" stroke={color} strokeWidth="1.5" strokeDasharray="3 2" />
      <path d="M6 5l4 3-4 3V5z" fill={color} fillOpacity="0.5" />
    </svg>
  );
}

function DotIcon({ color, size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none" style={{ flexShrink: 0 }}>
      <circle cx="8" cy="8" r="7" stroke={color} strokeWidth="1.5" strokeOpacity="0.4" />
      <circle cx="8" cy="8" r="2.5" fill={color} fillOpacity="0.4" />
    </svg>
  );
}

function StatusIcon({ status, tok, size = 16 }) {
  switch (status) {
    case 'running':  return <SpinnerIcon color={tok.running} size={size} />;
    case 'done':     return <CheckIcon   color={tok.success} size={size} />;
    case 'failed':   return <XIcon       color={tok.error}   size={size} />;
    case 'skipped':  return <SkipIcon    color={tok.skipped} size={size} />;
    default:         return <DotIcon     color={tok.pending} size={size} />;
  }
}

function statusColor(status, tok) {
  switch (status) {
    case 'running': return tok.running;
    case 'done':    return tok.success;
    case 'failed':  return tok.error;
    case 'skipped': return tok.skipped;
    default:        return tok.pending;
  }
}

// ─── Live duration ticker ─────────────────────────────────────────────────────
// Renders a running elapsed clock for 'running' steps, refreshing every 500ms.
function LiveDuration({ startedAt, tok }) {
  const [elapsed, setElapsed] = useState(startedAt ? Date.now() - startedAt : 0);

  useEffect(() => {
    if (!startedAt) return;
    const id = setInterval(() => setElapsed(Date.now() - startedAt), 500);
    return () => clearInterval(id);
  }, [startedAt]);

  return (
    <span style={{ color: tok.running, fontVariantNumeric: 'tabular-nums' }}>
      {formatDuration(elapsed)}
    </span>
  );
}

// ─── Tooltip ──────────────────────────────────────────────────────────────────
function Tooltip({ text, tok, children }) {
  const [visible, setVisible] = useState(false);
  return (
    <span style={{ position: 'relative', display: 'inline-flex' }}
      onMouseEnter={() => setVisible(true)}
      onMouseLeave={() => setVisible(false)}
    >
      {children}
      {visible && text && (
        <span style={{
          position: 'absolute',
          bottom: '120%',
          left: '50%',
          transform: 'translateX(-50%)',
          background: tok.tooltipBg,
          color: tok.tooltipFg,
          fontSize: '0.7rem',
          padding: '4px 8px',
          borderRadius: 5,
          whiteSpace: 'nowrap',
          maxWidth: 260,
          whiteSpace: 'pre-wrap',
          wordBreak: 'break-word',
          zIndex: 100,
          pointerEvents: 'none',
          animation: 'pt-fade-in 0.1s ease',
          boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
        }}>
          {text}
        </span>
      )}
    </span>
  );
}

// ─── Compact variant ──────────────────────────────────────────────────────────
function CompactProgressTracker({ steps, tok, showETA }) {
  const eta = showETA ? calcETA(steps) : null;
  const doneCount = steps.filter(s => s.status === 'done').length;
  const total = steps.length;
  const progressPct = total > 0 ? Math.round((doneCount / total) * 100) : 0;

  return (
    <div style={{
      display: 'inline-flex',
      flexDirection: 'column',
      gap: 8,
      background: tok.card,
      border: `1px solid ${tok.border}`,
      borderRadius: 12,
      padding: '10px 14px',
      boxShadow: tok.shadow,
      fontFamily: "'Space Grotesk', sans-serif",
      animation: 'pt-fade-in 0.2s ease',
      minWidth: 240,
    }}>
      {/* Pill row */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center' }}>
        {steps.map((step, i) => {
          const color = statusColor(step.status, tok);
          const isRunning = step.status === 'running';
          return (
            <Tooltip key={step.id} text={step.error || step.description || step.label} tok={tok}>
              <div style={{
                display: 'inline-flex',
                alignItems: 'center',
                gap: 5,
                background: isRunning ? tok.runningBg : 'transparent',
                border: `1px solid ${isRunning ? tok.running : tok.border}`,
                borderRadius: 20,
                padding: '3px 8px',
                fontSize: '0.75rem',
                fontWeight: 600,
                color: color,
                transition: 'all 0.2s',
                animation: isRunning ? 'pt-pulse 2s ease-in-out infinite' : 'none',
                cursor: step.error ? 'help' : 'default',
              }}>
                <StatusIcon status={step.status} tok={tok} size={12} />
                <span style={{ color: isRunning ? tok.fg : color }}>{step.label}</span>
                {isRunning && step.startedAt && (
                  <LiveDuration startedAt={step.startedAt} tok={tok} />
                )}
              </div>
            </Tooltip>
          );
        })}
      </div>

      {/* Progress bar */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <div style={{ flex: 1, height: 3, background: tok.connector, borderRadius: 2, overflow: 'hidden' }}>
          <div style={{
            height: '100%',
            width: `${progressPct}%`,
            background: progressPct === 100 ? tok.success : tok.running,
            borderRadius: 2,
            transition: 'width 0.4s ease',
          }} />
        </div>
        <span style={{ fontSize: '0.7rem', color: tok.fgMuted, fontVariantNumeric: 'tabular-nums' }}>
          {doneCount}/{total}
        </span>
        {showETA && eta != null && eta > 0 && (
          <span style={{ fontSize: '0.7rem', color: tok.fgMuted }}>
            ~{formatDuration(eta)} left
          </span>
        )}
      </div>
    </div>
  );
}

// ─── Expanded variant ─────────────────────────────────────────────────────────
function ExpandedProgressTracker({ steps, tok, showETA }) {
  const eta = showETA ? calcETA(steps) : null;
  const doneCount = steps.filter(s => s.status === 'done').length;
  const failedCount = steps.filter(s => s.status === 'failed').length;
  const total = steps.length;
  const progressPct = total > 0 ? Math.round((doneCount / total) * 100) : 0;

  // Overall status label
  const allDone = doneCount === total;
  const anyRunning = steps.some(s => s.status === 'running');
  const anyFailed = failedCount > 0;
  let overallLabel = anyFailed ? 'Failed' : allDone ? 'Complete' : anyRunning ? 'Running' : 'Pending';
  let overallColor = anyFailed ? tok.error : allDone ? tok.success : anyRunning ? tok.running : tok.pending;

  return (
    <div style={{
      background: tok.card,
      border: `1px solid ${tok.border}`,
      borderRadius: 12,
      padding: '1.1rem 1.25rem',
      boxShadow: tok.shadow,
      fontFamily: "'Space Grotesk', sans-serif",
      animation: 'pt-fade-in 0.2s ease',
      minWidth: 280,
    }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontWeight: 700, fontSize: '0.875rem', color: tok.fg }}>
            Progress
          </span>
          <span style={{
            fontSize: '0.68rem',
            fontWeight: 700,
            color: overallColor,
            background: overallColor + '1a',
            padding: '2px 7px',
            borderRadius: 10,
            letterSpacing: '0.04em',
            textTransform: 'uppercase',
          }}>
            {overallLabel}
          </span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          {showETA && eta != null && eta > 0 && (
            <span style={{ fontSize: '0.72rem', color: tok.fgMuted }}>
              ~{formatDuration(eta)} left
            </span>
          )}
          <span style={{ fontSize: '0.72rem', color: tok.fgMuted, fontVariantNumeric: 'tabular-nums' }}>
            {doneCount}/{total}
          </span>
        </div>
      </div>

      {/* Global progress bar */}
      <div style={{ height: 3, background: tok.connector, borderRadius: 2, overflow: 'hidden', marginBottom: '1.1rem' }}>
        <div style={{
          height: '100%',
          width: `${progressPct}%`,
          background: anyFailed ? tok.error : allDone ? tok.success : tok.running,
          borderRadius: 2,
          transition: 'width 0.5s ease',
        }} />
      </div>

      {/* Timeline */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
        {steps.map((step, i) => {
          const isLast = i === steps.length - 1;
          const color = statusColor(step.status, tok);
          const isRunning = step.status === 'running';
          const isDone = step.status === 'done';
          const isFailed = step.status === 'failed';
          const isSkipped = step.status === 'skipped';
          const duration = isDone && step.startedAt && step.completedAt
            ? step.completedAt - step.startedAt
            : null;

          return (
            <div key={step.id} style={{ display: 'flex', gap: 12 }}>
              {/* Left: icon + connector line */}
              <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: 20, flexShrink: 0 }}>
                <div style={{ marginTop: 2 }}>
                  <StatusIcon status={step.status} tok={tok} size={18} />
                </div>
                {!isLast && (
                  <div style={{
                    flex: 1,
                    width: 2,
                    background: isDone ? tok.success : isRunning ? tok.running : tok.connector,
                    borderRadius: 1,
                    margin: '3px 0',
                    minHeight: 16,
                    opacity: isSkipped ? 0.35 : 1,
                    // Dashed for skipped
                    ...(isSkipped ? {
                      backgroundImage: `repeating-linear-gradient(to bottom, ${tok.connector} 0, ${tok.connector} 4px, transparent 4px, transparent 8px)`,
                      background: 'none',
                    } : {}),
                  }} />
                )}
              </div>

              {/* Right: step content */}
              <div style={{
                flex: 1,
                paddingBottom: isLast ? 0 : '0.85rem',
                opacity: isSkipped ? 0.6 : 1,
              }}>
                {/* Step label row */}
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 2 }}>
                  <span style={{
                    fontWeight: 600,
                    fontSize: '0.84rem',
                    color: isRunning ? tok.fg : isDone ? tok.fg : isFailed ? tok.error : tok.fgMuted,
                    textDecoration: isSkipped ? 'line-through' : 'none',
                  }}>
                    {step.label}
                  </span>

                  {/* Duration badge */}
                  {isRunning && step.startedAt && (
                    <span style={{ fontSize: '0.7rem', color: tok.running, fontVariantNumeric: 'tabular-nums' }}>
                      <LiveDuration startedAt={step.startedAt} tok={tok} />
                    </span>
                  )}
                  {isDone && duration != null && (
                    <span style={{ fontSize: '0.7rem', color: tok.success, fontVariantNumeric: 'tabular-nums' }}>
                      {formatDuration(duration)}
                    </span>
                  )}
                  {isSkipped && (
                    <span style={{ fontSize: '0.68rem', color: tok.skipped, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                      skipped
                    </span>
                  )}
                </div>

                {/* Description */}
                {step.description && !isFailed && (
                  <div style={{ fontSize: '0.78rem', color: tok.fgMuted, lineHeight: 1.4 }}>
                    {step.description}
                  </div>
                )}

                {/* Error message (inline) */}
                {isFailed && step.error && (
                  <div style={{
                    marginTop: 4,
                    fontSize: '0.75rem',
                    color: tok.error,
                    background: tok.errorBg,
                    border: `1px solid ${tok.error}30`,
                    borderRadius: 6,
                    padding: '5px 8px',
                    lineHeight: 1.45,
                    fontFamily: "'Fira Code', monospace",
                  }}>
                    {step.error}
                  </div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── ProgressTracker (controlled) ────────────────────────────────────────────
function ProgressTracker({
  steps = [],
  currentStepId,
  variant = 'expanded',
  theme = 'light',
  showETA = false,
}) {
  injectPTStyles();

  const [resolvedTheme, setResolvedTheme] = useState(resolveTheme(theme));
  useEffect(() => {
    if (theme !== 'auto') { setResolvedTheme(theme); return; }
    const mq = window.matchMedia('(prefers-color-scheme: dark)');
    const handler = e => setResolvedTheme(e.matches ? 'dark' : 'light');
    mq.addEventListener('change', handler);
    return () => mq.removeEventListener('change', handler);
  }, [theme]);

  const tok = TOKENS[resolvedTheme] || TOKENS.light;

  // currentStepId drives status: if a step has status 'pending' but matches currentStepId,
  // promote it to running (allows simpler caller-side code)
  const resolvedSteps = steps.map(s =>
    currentStepId && s.id === currentStepId && s.status === 'pending'
      ? { ...s, status: 'running', startedAt: s.startedAt || Date.now() }
      : s
  );

  if (variant === 'compact') {
    return <CompactProgressTracker steps={resolvedSteps} tok={tok} showETA={showETA} />;
  }
  return <ExpandedProgressTracker steps={resolvedSteps} tok={tok} showETA={showETA} />;
}

// ─── ProgressTrackerContainer (AG-UI auto-wiring) ────────────────────────────
/**
 * Subscribes to AG-UI step events and maintains internal step list.
 * Supports dynamic step registration (STEP_STARTED adds new steps)
 * plus PROGRESS_UPDATE for partial description updates.
 *
 * Props:
 *   eventSource: EventTarget | EventEmitter
 *   initialSteps?: Step[]  — optional seed list
 *   variant, theme, showETA — passed through to ProgressTracker
 */
function ProgressTrackerContainer({
  eventSource,
  initialSteps = [],
  variant = 'expanded',
  theme = 'light',
  showETA = false,
}) {
  const [steps, setSteps] = useState(initialSteps);

  const updateStep = useCallback((id, patch) => {
    setSteps(prev => prev.map(s => s.id === id ? { ...s, ...patch } : s));
  }, []);

  const addOrUpdateStep = useCallback((id, patch) => {
    setSteps(prev => {
      const exists = prev.some(s => s.id === id);
      if (exists) return prev.map(s => s.id === id ? { ...s, ...patch } : s);
      return [...prev, { id, label: id, status: 'pending', ...patch }];
    });
  }, []);

  useEffect(() => {
    if (!eventSource) return;

    function onStarted(e) {
      const d = e.detail || e;
      addOrUpdateStep(d.stepId, {
        label: d.label || d.stepId,
        description: d.description,
        status: 'running',
        startedAt: Date.now(),
      });
    }
    function onCompleted(e) {
      const d = e.detail || e;
      updateStep(d.stepId, { status: 'done', completedAt: Date.now() });
    }
    function onFailed(e) {
      const d = e.detail || e;
      updateStep(d.stepId, { status: 'failed', completedAt: Date.now(), error: d.error });
    }
    function onSkipped(e) {
      const d = e.detail || e;
      updateStep(d.stepId, { status: 'skipped', completedAt: Date.now() });
    }
    function onProgress(e) {
      const d = e.detail || e;
      addOrUpdateStep(d.stepId, { description: d.description });
    }

    if (typeof eventSource.addEventListener === 'function') {
      eventSource.addEventListener('STEP_STARTED',    onStarted);
      eventSource.addEventListener('STEP_COMPLETED',  onCompleted);
      eventSource.addEventListener('STEP_FAILED',     onFailed);
      eventSource.addEventListener('STEP_SKIPPED',    onSkipped);
      eventSource.addEventListener('PROGRESS_UPDATE', onProgress);
      return () => {
        eventSource.removeEventListener('STEP_STARTED',    onStarted);
        eventSource.removeEventListener('STEP_COMPLETED',  onCompleted);
        eventSource.removeEventListener('STEP_FAILED',     onFailed);
        eventSource.removeEventListener('STEP_SKIPPED',    onSkipped);
        eventSource.removeEventListener('PROGRESS_UPDATE', onProgress);
      };
    }

    if (typeof eventSource.on === 'function') {
      eventSource.on('STEP_STARTED',    onStarted);
      eventSource.on('STEP_COMPLETED',  onCompleted);
      eventSource.on('STEP_FAILED',     onFailed);
      eventSource.on('STEP_SKIPPED',    onSkipped);
      eventSource.on('PROGRESS_UPDATE', onProgress);
      return () => {
        eventSource.off('STEP_STARTED',    onStarted);
        eventSource.off('STEP_COMPLETED',  onCompleted);
        eventSource.off('STEP_FAILED',     onFailed);
        eventSource.off('STEP_SKIPPED',    onSkipped);
        eventSource.off('PROGRESS_UPDATE', onProgress);
      };
    }
  }, [eventSource, addOrUpdateStep, updateStep]);

  return (
    <ProgressTracker
      steps={steps}
      variant={variant}
      theme={theme}
      showETA={showETA}
    />
  );
}

// Exports
if (typeof module !== 'undefined') {
  module.exports = { ProgressTracker, ProgressTrackerContainer };
}
