"use client";

/**
 * AgentTerminal component
 *
 * Read-only terminal view for displaying orchestrator agent output.
 * Uses a <pre> 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 <pre>.
// 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 (
      <span
        data-testid="status-indicator"
        data-status={status}
        style={{
          ...baseStyle,
          background: "var(--success)",
          animation: "agentPulse 1.5s ease-in-out infinite",
        }}
        aria-label="Running"
      />
    );
  }

  if (status === "completed") {
    return (
      <span
        data-testid="status-indicator"
        data-status={status}
        style={{
          ...baseStyle,
          background: "var(--muted)",
        }}
        aria-label="Completed"
      />
    );
  }

  // error
  return (
    <span
      data-testid="status-indicator"
      data-status={status}
      style={{
        ...baseStyle,
        background: "var(--danger)",
      }}
      aria-label="Error"
    />
  );
}

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

interface StatusBadgeProps {
  status: AgentStatus;
}

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

  const labelMap: Record<AgentStatus, string> = {
    spawning: "spawning",
    running: "running",
    completed: "completed",
    error: "error",
  };

  return (
    <span
      data-testid="status-badge"
      style={{
        fontSize: "0.65rem",
        fontFamily: "var(--mono)",
        color: colorMap[status],
        border: `1px solid ${colorMap[status]}`,
        borderRadius: 3,
        padding: "1px 5px",
        lineHeight: 1.6,
        letterSpacing: "0.03em",
        flexShrink: 0,
      }}
    >
      {labelMap[status]}
    </span>
  );
}

// ==========================================
// 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<HTMLPreElement>(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 (
    <div
      className={className}
      style={containerStyle}
      role="region"
      aria-label={`Agent output: ${agent.agentType}`}
      data-testid="agent-terminal"
      data-agent-id={agent.agentId}
    >
      {/* Header */}
      <div style={headerStyle} data-testid="agent-terminal-header">
        <StatusIndicator status={agent.status} />

        <span style={titleStyle} data-testid="agent-type-label">
          {agent.agentType}
          {agent.jobId !== undefined ? ` · ${agent.jobId}` : ""}
        </span>

        <StatusBadge status={agent.status} />

        <span style={durationStyle} data-testid="agent-duration">
          {duration}
        </span>

        {/* Copy button */}
        <button
          aria-label="Copy agent output"
          style={copyButtonStyle}
          onClick={handleCopy}
          data-testid="copy-button"
          onMouseEnter={(e): void => {
            if (!copied) {
              (e.currentTarget as HTMLButtonElement).style.color = "var(--text)";
              (e.currentTarget as HTMLButtonElement).style.borderColor = "var(--text-2)";
            }
          }}
          onMouseLeave={(e): void => {
            if (!copied) {
              (e.currentTarget as HTMLButtonElement).style.color = "var(--muted)";
              (e.currentTarget as HTMLButtonElement).style.borderColor = "var(--border)";
            }
          }}
        >
          {copied ? "copied" : "copy"}
        </button>
      </div>

      {/* Output area */}
      <pre
        ref={outputRef}
        style={outputStyle}
        data-testid="agent-output"
        aria-label="Agent output log"
        aria-live="polite"
        aria-atomic="false"
      >
        {agent.outputLines.length === 0 ? (
          <span style={{ color: "var(--muted)" }}>
            {agent.status === "spawning" ? "Spawning agent..." : "Waiting for output..."}
          </span>
        ) : (
          agent.outputLines.map(stripAnsi).join("")
        )}
      </pre>

      {/* Error message overlay */}
      {agent.status === "error" && agent.errorMessage !== undefined && (
        <div
          style={{
            padding: "4px 12px",
            fontSize: "0.7rem",
            fontFamily: "var(--mono)",
            color: "var(--danger)",
            background: "var(--bg-deep)",
            borderTop: "1px solid var(--border)",
            flexShrink: 0,
          }}
          data-testid="agent-error-message"
          role="alert"
        >
          Error: {agent.errorMessage}
        </div>
      )}

      {/* Pulse animation keyframes — injected inline via style tag for zero deps */}
      <style>{`
        @keyframes agentPulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.4; }
        }
      `}</style>
    </div>
  );
}
