/**
 * ActionTree — Manus-style right panel showing execution tree.
 * Displays tool calls, file operations, and terminal output in a
 * hierarchical timeline view instead of inline in chat messages.
 */

import { useState, useRef, useEffect } from "react";
import {
  ChevronRight,
  ChevronDown,
  Terminal,
  FileText,
  Search,
  Globe,
  Code,
  CheckCircle2,
  XCircle,
  Loader2,
  Clock,
  Eye,
  Copy,
  Maximize2,
  Minimize2,
} from "lucide-react";
import type { ChatMessage } from "@/hooks/useChat";
import { CodeDiffViewer } from "./CodeDiffViewer";

// Tool category mapping for icons and colors
const TOOL_CATEGORIES: Record<string, { icon: typeof Terminal; color: string; label: string }> = {
  bash: { icon: Terminal, color: "text-green-400", label: "Shell" },
  PowerShell: { icon: Terminal, color: "text-blue-400", label: "PowerShell" },
  read_file: { icon: FileText, color: "text-sky-400", label: "Read File" },
  write_file: { icon: FileText, color: "text-amber-400", label: "Write File" },
  edit_file: { icon: FileText, color: "text-orange-400", label: "Edit File" },
  multi_edit_file: { icon: FileText, color: "text-orange-400", label: "Multi Edit" },
  glob_search: { icon: Search, color: "text-purple-400", label: "Glob Search" },
  grep_search: { icon: Search, color: "text-purple-400", label: "Grep Search" },
  WebSearch: { icon: Globe, color: "text-cyan-400", label: "Web Search" },
  WebFetch: { icon: Globe, color: "text-cyan-400", label: "Fetch URL" },
  TodoWrite: { icon: Code, color: "text-primary", label: "Plan Update" },
  TodoRead: { icon: Code, color: "text-primary", label: "Plan Read" },
  StructuredOutput: { icon: Code, color: "text-indigo-400", label: "Output" },
  SendUserMessage: { icon: Code, color: "text-yellow-400", label: "Ask User" },
};

function getToolMeta(name: string) {
  return TOOL_CATEGORIES[name] || { icon: Code, color: "text-muted-foreground", label: name };
}

interface ToolCall {
  id: string;
  name: string;
  arguments: string;
  result?: string;
  isError?: boolean;
  isExecuting?: boolean;
  durationMs?: number;
}

// Extract a short summary from tool arguments
function getToolSummary(name: string, args: string): string {
  try {
    const parsed = JSON.parse(args);
    switch (name) {
      case "bash":
      case "PowerShell":
        return parsed.command?.slice(0, 80) || "...";
      case "read_file":
        return parsed.path?.split("/").pop() || parsed.path || "...";
      case "write_file":
      case "edit_file":
      case "multi_edit_file":
        return parsed.path?.split("/").pop() || parsed.path || "...";
      case "glob_search":
        return parsed.pattern || "...";
      case "grep_search":
        return parsed.pattern || parsed.query || "...";
      case "WebSearch":
        return parsed.query || "...";
      case "WebFetch":
        return parsed.url?.slice(0, 60) || "...";
      case "TodoWrite":
        return `${(parsed.todos || []).length} items`;
      default:
        return Object.values(parsed).filter(v => typeof v === "string").join(", ").slice(0, 60) || "...";
    }
  } catch {
    return args.slice(0, 60) || "...";
  }
}

interface ActionNodeProps {
  tool: ToolCall;
  index: number;
  isLast: boolean;
}

function ActionNode({ tool, index, isLast }: ActionNodeProps) {
  const [expanded, setExpanded] = useState(false);
  const meta = getToolMeta(tool.name);
  const Icon = meta.icon;
  const summary = getToolSummary(tool.name, tool.arguments);
  const [copied, setCopied] = useState(false);

  const statusIcon = tool.isExecuting ? (
    <Loader2 className="size-3 animate-spin text-primary" />
  ) : tool.isError ? (
    <XCircle className="size-3 text-destructive" />
  ) : (
    <CheckCircle2 className="size-3 text-green-500" />
  );

  const handleCopy = (text: string) => {
    navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 1500);
  };

  return (
    <div className="relative">
      {/* Timeline connector */}
      {!isLast && (
        <div className="absolute left-[15px] top-[28px] bottom-0 w-px bg-border" />
      )}

      {/* Node header */}
      <button
        onClick={() => setExpanded(!expanded)}
        className="flex items-start gap-2 w-full text-left px-2 py-1.5 rounded-md hover:bg-accent/50 transition-colors group"
      >
        {/* Timeline dot */}
        <div className={`mt-0.5 size-[30px] rounded-full border border-border bg-background flex items-center justify-center shrink-0 ${meta.color}`}>
          <Icon className="size-3.5" />
        </div>

        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-1.5">
            {expanded ? (
              <ChevronDown className="size-3 text-muted-foreground shrink-0" />
            ) : (
              <ChevronRight className="size-3 text-muted-foreground shrink-0" />
            )}
            <span className="text-xs font-medium">{meta.label}</span>
            {statusIcon}
            {tool.durationMs !== undefined && (
              <span className="text-[10px] text-muted-foreground/60 font-mono flex items-center gap-0.5">
                <Clock className="size-2.5" />
                {tool.durationMs < 1000
                  ? `${tool.durationMs}ms`
                  : `${(tool.durationMs / 1000).toFixed(1)}s`}
              </span>
            )}
          </div>
          <p className="text-[11px] text-muted-foreground truncate mt-0.5 font-mono">
            {summary}
          </p>
        </div>
      </button>

      {/* Expanded content */}
      {expanded && (
        <div className="ml-[39px] mr-2 mb-2 mt-1">
          {/* Input */}
          <div className="relative group/block">
            <div className="text-[10px] text-muted-foreground/60 mb-1 flex items-center gap-1">
              <Eye className="size-2.5" /> Input
              <button
                onClick={() => handleCopy(tool.arguments)}
                className="ml-auto opacity-0 group-hover/block:opacity-100 transition-opacity"
              >
                <Copy className="size-2.5 text-muted-foreground hover:text-foreground" />
              </button>
            </div>
            <pre className="text-[11px] font-mono bg-secondary/30 rounded-md p-2 overflow-x-auto max-h-[200px] overflow-y-auto whitespace-pre-wrap break-all text-foreground/80">
              {(() => {
                try {
                  return JSON.stringify(JSON.parse(tool.arguments), null, 2);
                } catch {
                  return tool.arguments;
                }
              })()}
            </pre>
          </div>

          {/* Output — with CodeDiffViewer for edit/write operations */}
          {tool.result && (
            <div className="relative group/block mt-2">
              {(tool.name === "edit_file" || tool.name === "multi_edit_file") && tool.result && !tool.isError ? (
                (() => {
                  // Try to extract old/new content from edit result
                  try {
                    const parsed = JSON.parse(tool.arguments);
                    const fileName = parsed.path?.split("/").pop() || parsed.path;
                    // Show diff viewer with result as the "after" content
                    return (
                      <CodeDiffViewer
                        oldContent={parsed.old_string || parsed.old_text || ""}
                        newContent={parsed.new_string || parsed.new_text || parsed.old_string || ""}
                        fileName={fileName}
                      />
                    );
                  } catch {
                    return null;
                  }
                })() || (
                  <pre className="text-[11px] font-mono bg-secondary/30 rounded-md p-2 overflow-x-auto max-h-[300px] overflow-y-auto whitespace-pre-wrap break-all text-foreground/80">
                    {tool.result.length > 5000 ? tool.result.slice(0, 5000) + "\n\n[...truncated]" : tool.result}
                  </pre>
                )
              ) : (
                <>
                  <div className="text-[10px] text-muted-foreground/60 mb-1 flex items-center gap-1">
                    <Terminal className="size-2.5" /> Output
                    {tool.isError && (
                      <span className="text-destructive text-[9px]">ERROR</span>
                    )}
                    <button
                      onClick={() => handleCopy(tool.result!)}
                      className="ml-auto opacity-0 group-hover/block:opacity-100 transition-opacity"
                    >
                      <Copy className="size-2.5 text-muted-foreground hover:text-foreground" />
                    </button>
                  </div>
                  <pre
                    className={`text-[11px] font-mono rounded-md p-2 overflow-x-auto max-h-[300px] overflow-y-auto whitespace-pre-wrap break-all ${
                      tool.isError
                        ? "bg-destructive/10 text-destructive/80 border border-destructive/20"
                        : "bg-secondary/30 text-foreground/80"
                    }`}
                  >
                    {tool.result.length > 5000
                      ? tool.result.slice(0, 5000) + "\n\n[...truncated]"
                      : tool.result}
                  </pre>
                </>
              )}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

interface ActionTreeProps {
  messages: ChatMessage[];
  isStreaming: boolean;
}

export function ActionTree({ messages, isStreaming }: ActionTreeProps) {
  const scrollRef = useRef<HTMLDivElement>(null);
  const [autoScroll, setAutoScroll] = useState(true);

  // Collect all tool calls from all assistant messages
  const allToolCalls: ToolCall[] = [];
  for (const msg of messages) {
    if (msg.role === "assistant" && msg.toolCalls) {
      for (const tc of msg.toolCalls) {
        allToolCalls.push(tc);
      }
    }
  }

  // Auto-scroll to bottom when new tool calls appear
  useEffect(() => {
    if (autoScroll && scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [allToolCalls.length, autoScroll]);

  const handleScroll = () => {
    const el = scrollRef.current;
    if (!el) return;
    const isAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 50;
    setAutoScroll(isAtBottom);
  };

  if (allToolCalls.length === 0) {
    return (
      <div className="h-full flex flex-col items-center justify-center text-muted-foreground/40 gap-2 p-4">
        <Terminal className="size-8" />
        <p className="text-xs text-center">
          Actions will appear here as the agent works
        </p>
      </div>
    );
  }

  return (
    <div className="h-full flex flex-col relative">
      {/* Header with progress indicators */}
      {(() => {
        const completed = allToolCalls.filter(tc => tc.result && !tc.isError && !tc.isExecuting).length;
        const errors = allToolCalls.filter(tc => tc.isError).length;
        const running = allToolCalls.filter(tc => tc.isExecuting).length;
        const total = allToolCalls.length;
        const pct = total > 0 ? Math.round((completed / total) * 100) : 0;
        return (
          <div className="border-b border-border">
            <div className="flex items-center justify-between px-3 py-2">
              <div className="flex items-center gap-2">
                <span className="text-xs font-semibold">Actions</span>
                <span className="text-[10px] text-muted-foreground font-mono bg-secondary/50 px-1.5 py-0.5 rounded">
                  {total}
                </span>
              </div>
              <div className="flex items-center gap-2">
                {completed > 0 && (
                  <span className="flex items-center gap-0.5 text-[10px] text-green-500">
                    <CheckCircle2 className="size-3" />{completed}
                  </span>
                )}
                {errors > 0 && (
                  <span className="flex items-center gap-0.5 text-[10px] text-destructive">
                    <XCircle className="size-3" />{errors}
                  </span>
                )}
                {running > 0 && (
                  <span className="flex items-center gap-0.5 text-[10px] text-primary">
                    <Loader2 className="size-3 animate-spin" />{running}
                  </span>
                )}
                {isStreaming && running === 0 && (
                  <span className="flex items-center gap-0.5 text-[10px] text-primary">
                    <Loader2 className="size-3 animate-spin" />thinking
                  </span>
                )}
              </div>
            </div>
            {/* Progress bar */}
            {total > 0 && (
              <div className="h-0.5 bg-secondary/30">
                <div
                  className={`h-full transition-all duration-300 ${errors > 0 ? 'bg-destructive/60' : 'bg-green-500/60'}`}
                  style={{ width: `${pct}%` }}
                />
              </div>
            )}
          </div>
        );
      })()}

      {/* Jump to live button */}
      {!autoScroll && (
        <button
          onClick={() => {
            setAutoScroll(true);
            scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: "smooth" });
          }}
          className="absolute bottom-4 right-4 z-10 flex items-center gap-1 px-2.5 py-1 rounded-full bg-primary text-primary-foreground text-[10px] font-medium shadow-lg hover:bg-primary/90 transition-colors"
        >
          <Loader2 className="size-3 animate-spin" />
          Jump to live
        </button>
      )}

      {/* Timeline */}
      <div
        ref={scrollRef}
        onScroll={handleScroll}
        className="flex-1 overflow-y-auto p-2 space-y-0.5"
      >
        {allToolCalls.map((tc, i) => (
          <ActionNode
            key={tc.id || i}
            tool={tc}
            index={i}
            isLast={i === allToolCalls.length - 1}
          />
        ))}
      </div>
    </div>
  );
}
