import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router';
import { X, Play, Bot, CircleCheck, CircleX, ExternalLink, Loader2 } from 'lucide-react';
import { Dialog, DialogOverlay, DialogPortal } from '@/components/ui/dialog';
import * as DialogPrimitive from '@radix-ui/react-dialog';
import { cardStyle } from '@/components/Card';
import { useClient } from 'hooks/useClient';
import { chatListPath } from '../../../ChatHistory/chat-routes';
import type { AgentSummary } from '../../useAgents';
import { TaskInputForm, type TaskInputDef } from './TaskRunnerModal';

// Starting an agent is not running a job: the POST returns as soon as the session is spawned, and the
// run itself is watched in /chat. So this modal has no output pane — the transcript is the output,
// and it lives somewhere that survives closing this dialog.
type Phase = 'ready' | 'starting' | 'started' | 'failed';

type AgentDetail = {
  description: string | null;
  model: string;
  inputs?: Record<string, TaskInputDef>;
};

type StartResult = { sessionKey: string; cwd: string; model: string };

type AgentRunnerModalProps = {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  agent: AgentSummary;
  entryName: string;
  /** Absolute on-disk path of the clicked entry — what the agent is pointed at. Never tilde form. */
  entryFullPath: string;
};

export const AgentRunnerModal = ({ open, onOpenChange, agent, entryName, entryFullPath }: AgentRunnerModalProps) => {
  const client = useClient();
  const navigate = useNavigate();
  const [phase, setPhase] = useState<Phase>('ready');
  const [detail, setDetail] = useState<AgentDetail | null>(null);
  const [inputDefs, setInputDefs] = useState<Record<string, TaskInputDef> | null>(null);
  const [formValues, setFormValues] = useState<Record<string, string>>({});
  const [result, setResult] = useState<StartResult | null>(null);
  const [error, setError] = useState<string | null>(null);

  // Which inputs the file browser already answered — shown as the target row, not as editable fields.
  const [autoFilledKeys, setAutoFilledKeys] = useState<Set<string>>(new Set());

  useEffect(() => {
    client.get<AgentDetail>(`/agents/${agent.dirName}`).then((data) => {
      const defs = data.inputs ?? {};
      setDetail(data);
      setInputDefs(defs);

      const initial: Record<string, string> = {};
      const filled = new Set<string>();
      for (const [key, def] of Object.entries(defs)) {
        // entry_path is the only autofill the file browser can answer for an agent, and it answers it
        // with the absolute path — tilde expansion is the server's job, never a prompt's.
        if (def.autofill === 'entry_path') {
          initial[key] = entryFullPath;
          filled.add(key);
        } else if (def.autofill === 'entry_name') {
          initial[key] = entryName;
          filled.add(key);
        } else if (def.default !== undefined) {
          initial[key] = def.default;
        }
      }
      setFormValues(initial);
      setAutoFilledKeys(filled);
    });
  }, [agent.dirName]);

  const handleRun = async () => {
    setPhase('starting');
    setError(null);
    try {
      const started = await client.post<StartResult>(`/agents/${agent.dirName}/run`, { inputs: formValues });
      setResult(started);
      setPhase('started');
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to start the agent');
      setPhase('failed');
    }
  };

  const openChat = () => {
    if (!result) return;
    onOpenChange(false);
    // The run's cwd IS its project group, so link to that group's list. Built here rather than handed
    // down from the server, which used to return a literal `/chat?cwd=…` and so kept its own stale copy
    // of the chat URL shape.
    navigate(chatListPath(result.cwd));
  };

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogPortal>
        <DialogOverlay className="z-[700] bg-black/60 backdrop-blur-sm" />
        <DialogPrimitive.Content
          onOpenAutoFocus={(ev) => ev.preventDefault()}
          className="fixed left-[50%] top-[50%] z-[700] translate-x-[-50%] translate-y-[-50%] flex flex-col overflow-hidden rounded-xl border-2 border-duck-dark/30 shadow-2xl w-[90vw] max-w-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
          style={cardStyle()}
        >
          {/* Header */}
          <div className="shrink-0 flex flex-col border-b border-duck-dark/10 bg-background/60">
            <div className="flex items-center gap-3 px-5 py-3">
              <Bot className="h-4 w-4 shrink-0 text-duck-teal" />
              <div className="flex-1 min-w-0">
                <span className="text-sm font-semibold text-duck-dark truncate block">{agent.name}</span>
                <span className="text-xs text-duck-dark/50 truncate block">{entryName}</span>
              </div>
              <DialogPrimitive.Close className="p-1.5 rounded-md text-duck-dark/40 hover:text-duck-dark hover:bg-duck-dark/5 transition-colors cursor-pointer">
                <X className="h-4 w-4" />
              </DialogPrimitive.Close>
            </div>
            {detail?.description && (
              <p className="px-5 pb-3 text-xs text-duck-dark/50 dark:text-foreground/50 -mt-1">{detail.description}</p>
            )}
          </div>

          {phase === 'started' && result ? (
            <div className="flex flex-col items-center justify-center gap-4 p-8 text-center">
              <CircleCheck className="h-10 w-10 text-duck-teal" />
              <div>
                <div className="text-base font-medium text-duck-dark dark:text-foreground">Agent running</div>
                <div className="text-sm text-duck-dark/50 dark:text-foreground/50 mt-1">
                  It keeps going whether or not this stays open. Watch it in the chat — newest session on top.
                </div>
              </div>
              <div className="flex items-center gap-3">
                <button
                  onClick={openChat}
                  className="flex items-center gap-2 px-5 py-2 rounded-lg bg-duck-teal text-white text-sm font-medium hover:bg-duck-teal/90 cursor-pointer"
                >
                  <ExternalLink className="h-4 w-4" /> Open chat
                </button>
                <button
                  onClick={() => onOpenChange(false)}
                  className="px-5 py-2 rounded-lg text-sm font-medium text-duck-dark/70 dark:text-foreground/70 hover:bg-duck-dark/5 dark:hover:bg-foreground/5 cursor-pointer"
                >
                  Close
                </button>
              </div>
            </div>
          ) : (
            <>
              {/* Target — the path the agent is pointed at, exactly as the server will receive it. */}
              <div className="px-5 py-3 border-b border-duck-dark/10 flex flex-col gap-1">
                <span className="text-xs font-medium text-duck-dark/70 dark:text-foreground/70">Target</span>
                <span className="text-xs font-mono break-all text-duck-dark dark:text-foreground">{entryFullPath}</span>
              </div>

              {inputDefs && (
                <TaskInputForm
                  inputDefs={inputDefs}
                  values={formValues}
                  onChange={(key, value) => setFormValues((prev) => ({ ...prev, [key]: value }))}
                  autoFilledKeys={autoFilledKeys}
                />
              )}

              {error && (
                <div className="px-5 py-3 flex items-center gap-2 text-sm text-red-500">
                  <CircleX className="h-4 w-4 shrink-0" />
                  {error}
                </div>
              )}

              <div className="shrink-0 flex items-center justify-center gap-3 border-t border-duck-dark/10 py-4">
                <button
                  onClick={handleRun}
                  disabled={!inputDefs || phase === 'starting'}
                  className="flex items-center gap-2 px-6 py-2.5 rounded-lg bg-duck-teal text-white font-medium text-sm hover:bg-duck-teal/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
                >
                  {phase === 'starting' ? <Loader2 className="h-4 w-4 animate-spin" /> : <Play className="h-4 w-4" />}
                  {phase === 'starting' ? 'Starting…' : 'Run agent'}
                </button>
              </div>
            </>
          )}
        </DialogPrimitive.Content>
      </DialogPortal>
    </Dialog>
  );
};
