"use client"; /** * AgentTerminal component * * Read-only terminal view for displaying orchestrator agent output. * Uses a
 element with monospace font rather than xterm.js because
 * this is read-only agent stdout/stderr, not an interactive PTY.
 *
 * Features:
 * - Displays accumulated output lines with basic ANSI color rendering
 * - Status badge (spinning/checkmark/X) indicating agent lifecycle
 * - Header bar with agent type, status, and elapsed duration
 * - Auto-scrolls to bottom as new output arrives
 * - Copy-to-clipboard button for full output
 */

import { useEffect, useRef, useState, useCallback } from "react";
import type { ReactElement, CSSProperties } from "react";
import type { AgentSession, AgentStatus } from "@/hooks/useAgentStream";

// ==========================================
// Types
// ==========================================

export interface AgentTerminalProps {
  /** The agent session to display */
  agent: AgentSession;
  /** Optional CSS class name for the outer container */
  className?: string;
  /** Optional inline style for the outer container */
  style?: CSSProperties;
}

// ==========================================
// ANSI color strip helper
// ==========================================

// Simple ANSI escape sequence stripper — produces readable plain text for 
.
// We strip rather than parse for security and simplicity in read-only display.
// eslint-disable-next-line no-control-regex
const ANSI_PATTERN = /\x1b\[[0-9;]*[mGKHF]/g;

function stripAnsi(text: string): string {
  return text.replace(ANSI_PATTERN, "");
}

// ==========================================
// Duration helper
// ==========================================

function formatDuration(startedAt: number, endedAt?: number): string {
  const elapsed = Math.floor(((endedAt ?? Date.now()) - startedAt) / 1000);
  if (elapsed < 60) return `${elapsed.toString()}s`;
  const minutes = Math.floor(elapsed / 60);
  const seconds = elapsed % 60;
  return `${minutes.toString()}m ${seconds.toString()}s`;
}

// ==========================================
// Status indicator
// ==========================================

interface StatusIndicatorProps {
  status: AgentStatus;
}

function StatusIndicator({ status }: StatusIndicatorProps): ReactElement {
  const baseStyle: CSSProperties = {
    display: "inline-block",
    width: 8,
    height: 8,
    borderRadius: "50%",
    flexShrink: 0,
  };

  if (status === "running" || status === "spawning") {
    return (
      
    );
  }

  if (status === "completed") {
    return (
      
    );
  }

  // error
  return (
    
  );
}

// ==========================================
// Status badge
// ==========================================

interface StatusBadgeProps {
  status: AgentStatus;
}

function StatusBadge({ status }: StatusBadgeProps): ReactElement {
  const colorMap: Record = {
    spawning: "var(--warn)",
    running: "var(--success)",
    completed: "var(--muted)",
    error: "var(--danger)",
  };

  const labelMap: Record = {
    spawning: "spawning",
    running: "running",
    completed: "completed",
    error: "error",
  };

  return (
    
      {labelMap[status]}
    
  );
}

// ==========================================
// Component
// ==========================================

/**
 * AgentTerminal renders accumulated agent output in a scrollable pre block.
 * It is intentionally read-only — no keyboard input is accepted.
 */
export function AgentTerminal({ agent, className = "", style }: AgentTerminalProps): ReactElement {
  const outputRef = useRef(null);
  const [copied, setCopied] = useState(false);
  const [tick, setTick] = useState(0);

  // ==========================================
  // Duration ticker — only runs while active
  // ==========================================

  useEffect(() => {
    if (agent.status === "running" || agent.status === "spawning") {
      const id = setInterval(() => {
        setTick((t) => t + 1);
      }, 1000);
      return (): void => {
        clearInterval(id);
      };
    }
    return undefined;
  }, [agent.status]);

  // Consume tick to avoid unused-var lint
  void tick;

  // ==========================================
  // Auto-scroll to bottom on new output
  // ==========================================

  useEffect(() => {
    const el = outputRef.current;
    if (el) {
      el.scrollTop = el.scrollHeight;
    }
  }, [agent.outputLines]);

  // ==========================================
  // Copy to clipboard
  // ==========================================

  const handleCopy = useCallback((): void => {
    const text = agent.outputLines.map(stripAnsi).join("");
    void navigator.clipboard.writeText(text).then(() => {
      setCopied(true);
      setTimeout(() => {
        setCopied(false);
      }, 2000);
    });
  }, [agent.outputLines]);

  // ==========================================
  // Styles
  // ==========================================

  const containerStyle: CSSProperties = {
    display: "flex",
    flexDirection: "column",
    height: "100%",
    background: "var(--bg-deep)",
    overflow: "hidden",
    ...style,
  };

  const headerStyle: CSSProperties = {
    display: "flex",
    alignItems: "center",
    gap: 8,
    padding: "6px 12px",
    borderBottom: "1px solid var(--border)",
    flexShrink: 0,
    background: "var(--bg-deep)",
  };

  const titleStyle: CSSProperties = {
    fontSize: "0.75rem",
    fontFamily: "var(--mono)",
    color: "var(--text)",
    flex: 1,
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
  };

  const durationStyle: CSSProperties = {
    fontSize: "0.65rem",
    fontFamily: "var(--mono)",
    color: "var(--muted)",
    flexShrink: 0,
  };

  const outputStyle: CSSProperties = {
    flex: 1,
    overflow: "auto",
    margin: 0,
    padding: "8px 12px",
    fontFamily: "var(--mono)",
    fontSize: "0.75rem",
    lineHeight: 1.5,
    color: "var(--text)",
    background: "var(--bg-deep)",
    whiteSpace: "pre-wrap",
    wordBreak: "break-all",
  };

  const copyButtonStyle: CSSProperties = {
    background: "transparent",
    border: "1px solid var(--border)",
    borderRadius: 3,
    color: copied ? "var(--success)" : "var(--muted)",
    cursor: "pointer",
    fontSize: "0.65rem",
    fontFamily: "var(--mono)",
    padding: "2px 6px",
    flexShrink: 0,
  };

  const duration = formatDuration(agent.startedAt, agent.endedAt);

  return (
    
{/* Header */}
{agent.agentType} {agent.jobId !== undefined ? ` · ${agent.jobId}` : ""} {duration} {/* Copy button */}
{/* Output area */}
        {agent.outputLines.length === 0 ? (
          
            {agent.status === "spawning" ? "Spawning agent..." : "Waiting for output..."}
          
        ) : (
          agent.outputLines.map(stripAnsi).join("")
        )}
      
{/* Error message overlay */} {agent.status === "error" && agent.errorMessage !== undefined && (
Error: {agent.errorMessage}
)} {/* Pulse animation keyframes — injected inline via style tag for zero deps */}
); }