"use client";

import { useEffect, useRef, useState, type ReactNode } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import type { AgentDefinition } from "@/lib/sag/agents";
import { snapshotForAgentCall } from "@/lib/agents/context-snapshot";
import type { AgentMessage } from "@/lib/agents/threads";
import {
  CouncilVetoToggle,
  CouncilDissentBanner,
  buildPreflightContext,
  runPreflightCouncil,
  type CouncilVerdict,
} from "@/components/app/council-veto-toggle";

interface Message {
  role: "user" | "assistant";
  content: string;
  timestamp?: string;
}

interface Usage {
  inputTokens: number;
  outputTokens: number;
  cacheHit: number;
  cacheCreate: number;
}

/**
 * Optional per-message render slot. Called once per rendered message; the
 * caller decides what (if anything) to render. Used by the Marketing Agent
 * page to surface a "Send to social composer" button on assistant messages.
 */
export type AgentChatMessageActions = (message: AgentMessage) => ReactNode;

interface AgentChatProps {
  agent: AgentDefinition;
  /** Optional seed messages — used when restoring a saved thread. */
  initialMessages?: AgentMessage[];
  /** Optional callback fired whenever the message list changes. */
  onMessagesChange?: (msgs: AgentMessage[]) => void;
  /** Optional thread id. When this changes, the chat re-seeds from initialMessages. */
  threadId?: string;
  /**
   * Optional render slot evaluated for each message inside its card. When
   * undefined, messages render exactly as before. The function receives the
   * full `AgentMessage` (role + content + timestamp) so callers can branch
   * on the role.
   */
  messageActions?: AgentChatMessageActions;
  /**
   * When true, renders the LLM Council veto-check toggle above the composer.
   * When the toggle is on, a preflight council consult runs in parallel with
   * the main agent call; high-confidence dissent is surfaced as an inline
   * banner above the in-flight assistant response.
   */
  enableCouncilVeto?: boolean;
}

export function AgentChat({
  agent,
  initialMessages,
  onMessagesChange,
  threadId,
  messageActions,
  enableCouncilVeto,
}: AgentChatProps) {
  const [messages, setMessages] = useState<Message[]>(() =>
    initialMessages
      ? initialMessages.map((m) => ({ role: m.role, content: m.content, timestamp: m.timestamp }))
      : []
  );
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");
  const [usage, setUsage] = useState<Usage | null>(null);
  const scroller = useRef<HTMLDivElement>(null);
  const lastSeededThreadId = useRef<string | undefined>(threadId);

  // Council veto state (only meaningful when enableCouncilVeto === true).
  const [vetoOn, setVetoOn] = useState(false);
  const [preflightInFlight, setPreflightInFlight] = useState(false);
  const [preflightVerdicts, setPreflightVerdicts] = useState<CouncilVerdict[]>([]);
  // Bumped on every send so the banner remounts and re-reads its dismissed state.
  const [preflightAttempt, setPreflightAttempt] = useState(0);

  // Re-seed when the thread switches. Skip the first run (already seeded via useState
  // initializer) and defer to avoid synchronous setState-in-effect.
  useEffect(() => {
    if (lastSeededThreadId.current === threadId) return;
    lastSeededThreadId.current = threadId;
    const handle = setTimeout(() => {
      setMessages(
        initialMessages
          ? initialMessages.map((m) => ({ role: m.role, content: m.content, timestamp: m.timestamp }))
          : []
      );
      setError("");
      setUsage(null);
      // Dissent banner dismissal is intentionally per-thread: clear it on
      // thread switch and keep the toggle preference itself (vetoOn).
      setPreflightVerdicts([]);
      setPreflightInFlight(false);
      setPreflightAttempt(0);
    }, 0);
    return () => clearTimeout(handle);
    // We intentionally depend on threadId — initialMessages identity changes on every parent render.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [threadId]);

  useEffect(() => {
    scroller.current?.scrollTo({ top: scroller.current.scrollHeight, behavior: "smooth" });
  }, [messages.length, loading]);

  function emitChange(next: Message[]) {
    if (!onMessagesChange) return;
    onMessagesChange(
      next.map((m) => ({
        role: m.role,
        content: m.content,
        timestamp: m.timestamp ?? new Date().toISOString(),
      }))
    );
  }

  async function send(content: string) {
    if (!content.trim()) return;
    const userMsg: Message = { role: "user", content, timestamp: new Date().toISOString() };
    const next: Message[] = [...messages, userMsg];
    setMessages(next);
    emitChange(next);
    setInput("");
    setLoading(true);
    setError("");

    // Fire-and-forget preflight council consult. Runs in parallel with the
    // main agent call — never blocks it. The dissent banner appears above
    // the in-flight assistant message as soon as the council returns.
    if (enableCouncilVeto && vetoOn) {
      setPreflightVerdicts([]);
      setPreflightInFlight(true);
      setPreflightAttempt((n) => n + 1);
      const ctx = buildPreflightContext(messages);
      runPreflightCouncil({
        message: content,
        context: ctx,
        agentLabel: agent.name,
      })
        .then((verdicts) => {
          setPreflightVerdicts(verdicts);
        })
        .catch(() => {
          // Silent on error — main agent call is unaffected.
          setPreflightVerdicts([]);
        })
        .finally(() => {
          setPreflightInFlight(false);
        });
    }

    try {
      const resp = await fetch(`/api/agents/${agent.id}`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          messages: next.map((m) => ({ role: m.role, content: m.content })),
          contextSnapshot: snapshotForAgentCall(),
        }),
      });
      const data = await resp.json();
      if (!resp.ok) throw new Error(data.error || "Request failed");
      const assistantMsg: Message = {
        role: "assistant",
        content: data.message,
        timestamp: new Date().toISOString(),
      };
      const after: Message[] = [...next, assistantMsg];
      setMessages(after);
      emitChange(after);
      if (data.usage) {
        setUsage({
          inputTokens: data.usage.input_tokens,
          outputTokens: data.usage.output_tokens,
          cacheHit: data.usage.cache_read_input_tokens,
          cacheCreate: data.usage.cache_creation_input_tokens,
        });
      }
    } catch (e) {
      setError(e instanceof Error ? e.message : "Unknown error");
    } finally {
      setLoading(false);
    }
  }

  return (
    <main className="flex-1 flex flex-col bg-muted/20">
      <div className="flex items-center gap-2 px-8 py-3 border-b bg-background text-xs">
        <Badge variant="info">{agent.model}</Badge>
        <span className="text-muted-foreground">SAG portfolio context loaded · prompt caching enabled</span>
        {usage && (
          <span className="ml-auto font-mono text-muted-foreground">
            in {usage.inputTokens} · out {usage.outputTokens} · cache hit {usage.cacheHit}
            {usage.cacheCreate > 0 ? ` · cache create ${usage.cacheCreate}` : ""}
          </span>
        )}
      </div>

      <div ref={scroller} className="flex-1 overflow-y-auto px-8 py-6 space-y-4">
        {messages.length === 0 ? (
          <div className="max-w-3xl mx-auto">
            <div className="flex items-center gap-3">
              <span className="text-3xl">{agent.icon}</span>
              <div>
                <h2 className="text-xl font-semibold">{agent.name}</h2>
                <p className="text-sm text-muted-foreground">{agent.subtitle}</p>
              </div>
            </div>
            <p className="mt-4 text-sm text-muted-foreground">Try one of these to get started:</p>
            <div className="mt-3 grid sm:grid-cols-2 gap-3">
              {agent.suggestions.map((s) => (
                <button
                  key={s}
                  onClick={() => send(s)}
                  className="text-left rounded-md border bg-background p-3 text-sm hover:bg-accent transition-colors"
                >
                  {s}
                </button>
              ))}
            </div>
            <p className="mt-6 text-xs text-muted-foreground">
              Requires ANTHROPIC_API_KEY in .env.local. The agent will tell you what isn’t connected yet rather than guessing.
            </p>
          </div>
        ) : (
          messages.map((m, i) => {
            const actions = messageActions
              ? messageActions({
                  role: m.role,
                  content: m.content,
                  timestamp: m.timestamp ?? new Date().toISOString(),
                })
              : null;
            return (
              <div key={i} className={`max-w-3xl mx-auto flex ${m.role === "user" ? "justify-end" : "justify-start"}`}>
                <Card className={m.role === "user" ? "bg-[var(--sag-brand)] text-white border-0 max-w-[80%]" : "max-w-[80%]"}>
                  <CardContent className="p-4">
                    <div className={`text-xs mb-1 ${m.role === "user" ? "text-white/70" : "text-muted-foreground"}`}>
                      {m.role === "user" ? "You" : agent.name}
                    </div>
                    <div className="text-sm whitespace-pre-wrap leading-relaxed">{m.content}</div>
                    {actions}
                  </CardContent>
                </Card>
              </div>
            );
          })
        )}
        {enableCouncilVeto && preflightVerdicts.length > 0 && (
          <CouncilDissentBanner key={preflightAttempt} verdicts={preflightVerdicts} />
        )}
        {loading && (
          <div className="max-w-3xl mx-auto flex justify-start">
            <Card>
              <CardContent className="p-4 text-sm text-muted-foreground">Thinking…</CardContent>
            </Card>
          </div>
        )}
        {error && (
          <div className="max-w-3xl mx-auto rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive">
            {error}
          </div>
        )}
      </div>

      {enableCouncilVeto && (
        <CouncilVetoToggle
          on={vetoOn}
          onChange={setVetoOn}
          preflightInFlight={preflightInFlight}
        />
      )}

      <form
        onSubmit={(e) => {
          e.preventDefault();
          send(input);
        }}
        className="px-8 py-4 border-t bg-background flex gap-2 max-w-3xl mx-auto w-full"
      >
        <Input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder={loading ? "Waiting for response..." : `Ask the ${agent.name}...`}
          disabled={loading}
          className="flex-1"
        />
        <Button type="submit" variant="brand" disabled={loading || !input.trim()}>
          Send
        </Button>
      </form>
    </main>
  );
}
