"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import {
  Zap,
  Send,
  Plus,
  Trash2,
  Loader2,
  Clock,
  DollarSign,
  MessageSquare,
  CheckCircle,
  XCircle,
  AlertTriangle,
  AlertCircle,
  X,
  Download,
} from "lucide-react";

interface FleetAction {
  deviceId: string;
  deviceName: string;
  type: "pull_model" | "remove_model" | "restart_engine";
  payload: Record<string, string>;
  reasoning: string;
}

interface Message {
  id: string;
  role: "user" | "assistant";
  content: string;
  model?: string;
  costUsd?: number;
  latencyMs?: number;
  createdAt?: string;
  intent?: string;
  actions?: FleetAction[];
}

interface Conversation {
  id: string;
  title: string;
  messageCount: number;
  updatedAt: string;
}

interface TokenAlert {
  id: string;
  severity: "info" | "warning" | "critical";
  category: string;
  title: string;
  message: string;
  deviceId?: string;
  acknowledged: boolean;
}

export default function TokenPage() {
  const [conversations, setConversations] = useState<Conversation[]>([]);
  const [activeConversationId, setActiveConversationId] = useState<string | null>(null);
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState("");
  const [sending, setSending] = useState(false);
  const [streaming, setStreaming] = useState(false);
  const [loadingConversation, setLoadingConversation] = useState(false);
  const [error, setError] = useState("");
  const [sidebarOpen, setSidebarOpen] = useState(true);
  const [alerts, setAlerts] = useState<TokenAlert[]>([]);
  const [exportOpen, setExportOpen] = useState(false);
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const loadAbortRef = useRef<AbortController | null>(null);
  const exportRef = useRef<HTMLDivElement>(null);

  const scrollToBottom = useCallback(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  }, []);

  // Close export dropdown on outside click
  useEffect(() => {
    function handleClickOutside(e: MouseEvent) {
      if (exportRef.current && !exportRef.current.contains(e.target as Node)) {
        setExportOpen(false);
      }
    }
    if (exportOpen) {
      document.addEventListener("mousedown", handleClickOutside);
      return () => document.removeEventListener("mousedown", handleClickOutside);
    }
  }, [exportOpen]);

  function triggerExport(format: "json" | "csv") {
    if (!activeConversationId) return;
    setExportOpen(false);
    const url = `/api/token/conversations/${activeConversationId}/export?format=${format}`;
    const a = document.createElement("a");
    a.href = url;
    a.download = "";
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  }

  // Load conversations
  useEffect(() => {
    fetch("/api/token/conversations")
      .then((r) => r.json())
      .then((data) => setConversations(data.conversations ?? []))
      .catch(() => {});
  }, []);

  // Load alerts once on mount
  useEffect(() => {
    fetch("/api/token/alerts")
      .then((r) => r.json())
      .then((data) => setAlerts(data.alerts ?? []))
      .catch(() => {});
  }, []);

  // Scroll on new messages
  useEffect(() => {
    scrollToBottom();
  }, [messages, scrollToBottom]);

  function startNewConversation() {
    setActiveConversationId(null);
    setMessages([]);
    setInput("");
    setError("");
    inputRef.current?.focus();
  }

  async function loadConversation(id: string) {
    // Abort any in-flight conversation load to prevent race conditions
    loadAbortRef.current?.abort();
    const controller = new AbortController();
    loadAbortRef.current = controller;

    setActiveConversationId(id);
    setMessages([]);
    setError("");
    setLoadingConversation(true);

    try {
      const res = await fetch(`/api/token/conversations/${id}`, {
        signal: controller.signal,
      });
      const data = await res.json();

      if (!res.ok) {
        setError(data.error || "Failed to load conversation");
        return;
      }

      const loaded: Message[] = (data.conversation?.messages ?? []).map(
        (m: { id: string; role: string; content: string; model?: string | null; costUsd?: number | null; latencyMs?: number | null; createdAt?: string }) => ({
          id: m.id,
          role: m.role as "user" | "assistant",
          content: m.content,
          model: m.model ?? undefined,
          costUsd: m.costUsd ?? undefined,
          latencyMs: m.latencyMs ?? undefined,
          createdAt: m.createdAt,
        }),
      );
      setMessages(loaded);
    } catch (err) {
      if ((err as Error).name === "AbortError") return;
      setError("Failed to load conversation. Check your connection.");
    } finally {
      setLoadingConversation(false);
    }
  }

  async function deleteConversation(id: string) {
    await fetch(`/api/token/conversations?id=${id}`, { method: "DELETE" });
    setConversations((prev) => prev.filter((c) => c.id !== id));
    if (activeConversationId === id) {
      startNewConversation();
    }
  }

  async function dismissAlert(alertId: string) {
    setAlerts((prev) =>
      prev.map((a) => (a.id === alertId ? { ...a, acknowledged: true } : a)),
    );
    try {
      await fetch("/api/token/alerts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ alertId }),
      });
    } catch {
      // Optimistic update — ignore POST failure
    }
  }

  // Visible alerts: unacknowledged warnings and criticals
  const visibleAlerts = alerts.filter(
    (a) => !a.acknowledged && (a.severity === "critical" || a.severity === "warning"),
  );

  async function sendMessage(e: React.FormEvent) {
    e.preventDefault();
    const msg = input.trim();
    if (!msg || sending) return;

    setInput("");
    setError("");
    setSending(true);

    // Add user message optimistically
    const userMsg: Message = {
      id: `tmp-${Date.now()}`,
      role: "user",
      content: msg,
    };
    setMessages((prev) => [...prev, userMsg]);

    const assistantId = `resp-${Date.now()}`;

    try {
      const res = await fetch("/api/token/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          message: msg,
          conversationId: activeConversationId,
          stream: true,
        }),
      });

      // Error responses are still JSON (non-2xx)
      if (!res.ok) {
        const data = await res.json();
        setError(data.error || "Token failed to respond");
        return;
      }

      // Add empty assistant message that will be filled by streaming tokens
      setMessages((prev) => [...prev, { id: assistantId, role: "assistant", content: "" }]);

      const reader = res.body!.getReader();
      const decoder = new TextDecoder();
      let buffer = "";

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });

        // Split on double newline (SSE event boundary) but handle partial lines
        const lines = buffer.split("\n");
        buffer = lines.pop() ?? "";

        for (const line of lines) {
          const trimmed = line.trim();
          if (!trimmed.startsWith("data: ")) continue;

          const jsonStr = trimmed.slice(6);
          if (jsonStr === "[DONE]") continue;

          try {
            const event = JSON.parse(jsonStr);

            // First event contains conversationId
            if (event.conversationId && !event.done) {
              if (!activeConversationId) {
                setActiveConversationId(event.conversationId);
                // Refresh conversation list for sidebar
                fetch("/api/token/conversations")
                  .then((r) => r.json())
                  .then((d) => setConversations(d.conversations ?? []))
                  .catch(() => {});
              }
              // Capture intent from init event
              if (event.intent) {
                setMessages((prev) =>
                  prev.map((m) =>
                    m.id === assistantId ? { ...m, intent: event.intent } : m,
                  ),
                );
              }
              continue;
            }

            // Final event with metadata
            if (event.done) {
              setMessages((prev) =>
                prev.map((m) =>
                  m.id === assistantId
                    ? {
                        ...m,
                        model: event.model,
                        costUsd: event.costUsd,
                        latencyMs: event.latencyMs,
                        intent: event.intent,
                        actions: event.actions,
                      }
                    : m,
                ),
              );
              continue;
            }

            // Token delta — append to assistant message content
            if (event.token) {
              setStreaming(true);
              setMessages((prev) =>
                prev.map((m) =>
                  m.id === assistantId
                    ? { ...m, content: m.content + event.token }
                    : m,
                ),
              );
            }
          } catch {
            // Skip malformed SSE lines
          }
        }
      }
    } catch {
      setError("Failed to reach Token. Check your connection.");
    } finally {
      setSending(false);
      setStreaming(false);
    }
  }

  async function handleApproveAction(action: FleetAction) {
    try {
      const res = await fetch("/api/token/actions", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ actionId: action.deviceId, status: "approved" }),
      });
      if (res.ok) {
        setMessages((prev) =>
          prev.map((m) => ({
            ...m,
            actions: m.actions?.filter((a) => a !== action),
          })),
        );
      }
    } catch {
      setError("Failed to approve action");
    }
  }

  async function handleRejectAction(action: FleetAction) {
    try {
      await fetch("/api/token/actions", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ actionId: action.deviceId, status: "rejected" }),
      });
      setMessages((prev) =>
        prev.map((m) => ({
          ...m,
          actions: m.actions?.filter((a) => a !== action),
        })),
      );
    } catch {
      setError("Failed to reject action");
    }
  }

  function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>) {
    if (e.key === "Enter" && !e.shiftKey) {
      e.preventDefault();
      sendMessage(e as unknown as React.FormEvent);
    }
  }

  return (
    <div className="flex h-[calc(100vh-8rem)] gap-4">
      {/* Conversation sidebar */}
      {sidebarOpen && (
        <div className="hidden w-64 shrink-0 flex-col md:flex">
          <div className="mb-3 flex items-center justify-between">
            <h2 className="text-sm font-semibold text-gray-300">Conversations</h2>
            <Button variant="ghost" size="sm" onClick={startNewConversation}>
              <Plus size={16} />
            </Button>
          </div>
          <div className="flex-1 space-y-1 overflow-y-auto">
            {conversations.length === 0 ? (
              <p className="px-2 py-4 text-center text-xs text-gray-500">
                No conversations yet. Start chatting below.
              </p>
            ) : (
              conversations.map((c) => (
                <div
                  key={c.id}
                  className={`group flex cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm transition-colors ${
                    activeConversationId === c.id
                      ? "bg-indigo-500/10 text-indigo-300"
                      : "text-gray-400 hover:bg-gray-800 hover:text-gray-200"
                  }`}
                  onClick={() => loadConversation(c.id)}
                >
                  <span className="truncate">{c.title}</span>
                  <button
                    onClick={(e) => {
                      e.stopPropagation();
                      deleteConversation(c.id);
                    }}
                    className="hidden shrink-0 text-gray-600 hover:text-red-400 group-hover:block"
                  >
                    <Trash2 size={14} />
                  </button>
                </div>
              ))
            )}
          </div>
        </div>
      )}

      {/* Chat area */}
      <div className="flex flex-1 flex-col">
        {/* Header */}
        <div className="mb-4 flex items-center gap-3">
          <div className="flex h-10 w-10 items-center justify-center rounded-full bg-indigo-500/20">
            <Zap className="h-5 w-5 text-indigo-400" />
          </div>
          <div>
            <h1 className="text-lg font-bold text-gray-100">Token</h1>
            <p className="text-xs text-gray-500">
              Your AI fleet assistant — model recommendations, deployment, and optimization
            </p>
          </div>
          <div className="ml-auto flex items-center gap-1">
            {activeConversationId && (
              <div ref={exportRef} className="relative">
                <button
                  onClick={() => setExportOpen((v) => !v)}
                  className="rounded-lg p-2 text-gray-500 hover:bg-gray-800 hover:text-gray-300"
                  aria-label="Export conversation"
                  title="Export conversation"
                >
                  <Download size={18} />
                </button>
                {exportOpen && (
                  <div className="absolute right-0 top-full z-50 mt-1 w-36 rounded-lg border border-gray-700 bg-gray-800 py-1 shadow-lg">
                    <button
                      onClick={() => triggerExport("json")}
                      className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-gray-300 hover:bg-gray-700"
                    >
                      Export JSON
                    </button>
                    <button
                      onClick={() => triggerExport("csv")}
                      className="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-gray-300 hover:bg-gray-700"
                    >
                      Export CSV
                    </button>
                  </div>
                )}
              </div>
            )}
            <button
              onClick={() => setSidebarOpen(!sidebarOpen)}
              className="hidden rounded-lg p-2 text-gray-500 hover:bg-gray-800 hover:text-gray-300 md:block"
            >
              <MessageSquare size={18} />
            </button>
          </div>
        </div>

        {/* Alert badges bar */}
        {visibleAlerts.length > 0 && (
          <div className="mb-2 flex flex-wrap gap-2">
            {visibleAlerts.map((alert) => (
              <div
                key={alert.id}
                className={`flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium ${
                  alert.severity === "critical"
                    ? "bg-red-500/15 text-red-400"
                    : "bg-yellow-500/15 text-yellow-400"
                }`}
              >
                {alert.severity === "critical" ? (
                  <AlertCircle size={12} className="shrink-0" />
                ) : (
                  <AlertTriangle size={12} className="shrink-0" />
                )}
                <span className="truncate max-w-[200px]">{alert.title}</span>
                <button
                  onClick={() => dismissAlert(alert.id)}
                  className={`ml-1 shrink-0 rounded-full p-0.5 transition-colors ${
                    alert.severity === "critical"
                      ? "hover:bg-red-500/20 hover:text-red-300"
                      : "hover:bg-yellow-500/20 hover:text-yellow-300"
                  }`}
                  aria-label={`Dismiss alert: ${alert.title}`}
                >
                  <X size={10} />
                </button>
              </div>
            ))}
          </div>
        )}

        {/* Messages */}
        <Card className="flex-1 overflow-hidden">
          <div className="flex h-full flex-col">
            <div className="flex-1 overflow-y-auto p-4">
              {loadingConversation ? (
                <div className="flex h-full flex-col items-center justify-center gap-3 text-center">
                  <Loader2 className="h-8 w-8 animate-spin text-indigo-400" />
                  <p className="text-sm text-gray-500">Loading conversation...</p>
                </div>
              ) : messages.length === 0 ? (
                <div className="flex h-full flex-col items-center justify-center gap-4 text-center">
                  <div className="flex h-16 w-16 items-center justify-center rounded-full bg-indigo-500/10">
                    <Zap className="h-8 w-8 text-indigo-400" />
                  </div>
                  <div>
                    <h2 className="text-lg font-semibold text-gray-200">
                      Hey, I&apos;m Token
                    </h2>
                    <p className="mt-1 max-w-md text-sm text-gray-500">
                      I can help you pick the right models for your hardware,
                      optimize your fleet deployment, and understand your cost savings.
                      What would you like to know?
                    </p>
                  </div>
                  <div className="mt-4 flex flex-wrap justify-center gap-2">
                    {[
                      "What models should I run on my hardware?",
                      "How much am I saving vs cloud APIs?",
                      "Help me set up a multi-model deployment",
                      "What's the best coding model for 8GB VRAM?",
                    ].map((suggestion) => (
                      <button
                        key={suggestion}
                        onClick={() => {
                          setInput(suggestion);
                          inputRef.current?.focus();
                        }}
                        className="rounded-lg border border-gray-700 bg-gray-800/50 px-3 py-2 text-xs text-gray-400 transition-colors hover:border-indigo-500/30 hover:text-gray-200"
                      >
                        {suggestion}
                      </button>
                    ))}
                  </div>
                </div>
              ) : (
                <div className="space-y-4">
                  {messages.map((msg) => (
                    <div
                      key={msg.id}
                      className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}
                    >
                      <div
                        className={`max-w-[80%] rounded-lg px-4 py-3 ${
                          msg.role === "user"
                            ? "bg-indigo-500/20 text-gray-100"
                            : "bg-gray-800 text-gray-200"
                        }`}
                      >
                        {msg.role === "assistant" ? (
                          <div className="prose prose-sm prose-invert max-w-none prose-pre:bg-transparent prose-pre:p-0">
                            <ReactMarkdown
                              remarkPlugins={[remarkGfm]}
                              components={{
                                code: ({ className, children, ...props }) => {
                                  const isBlock = className?.includes("language-");
                                  return isBlock ? (
                                    <code
                                      className={`${className} block overflow-x-auto rounded bg-gray-900 p-3 text-sm`}
                                      {...props}
                                    >
                                      {children}
                                    </code>
                                  ) : (
                                    <code
                                      className="rounded bg-gray-900 px-1.5 py-0.5 text-sm text-gray-300"
                                      {...props}
                                    >
                                      {children}
                                    </code>
                                  );
                                },
                                pre: ({ children }) => (
                                  <pre className="overflow-x-auto rounded-lg border border-gray-700 bg-gray-900 p-0">
                                    {children}
                                  </pre>
                                ),
                                a: ({ href, children }) => (
                                  <a
                                    href={href}
                                    target="_blank"
                                    rel="noopener noreferrer"
                                    className="text-indigo-400 hover:text-indigo-300"
                                  >
                                    {children}
                                  </a>
                                ),
                              }}
                            >
                              {msg.content}
                            </ReactMarkdown>
                          </div>
                        ) : (
                          <div className="whitespace-pre-wrap text-sm">
                            {msg.content}
                          </div>
                        )}
                        {msg.role === "assistant" && (msg.model || msg.latencyMs || msg.costUsd) && (
                          <div className="mt-2 flex items-center gap-3 border-t border-gray-700/50 pt-2 text-[10px] text-gray-500">
                            {msg.model && <span>{msg.model}</span>}
                            {msg.intent && msg.intent !== "chat" && (
                              <span className="rounded bg-indigo-500/20 px-1.5 py-0.5 text-[10px] text-indigo-300">
                                {msg.intent}
                              </span>
                            )}
                            {msg.latencyMs != null && (
                              <span className="flex items-center gap-1">
                                <Clock size={10} /> {(msg.latencyMs / 1000).toFixed(1)}s
                              </span>
                            )}
                            {msg.costUsd != null && msg.costUsd > 0 && (
                              <span className="flex items-center gap-1">
                                <DollarSign size={10} /> ${msg.costUsd.toFixed(4)}
                              </span>
                            )}
                          </div>
                        )}
                        {msg.actions && msg.actions.length > 0 && (
                          <div className="mt-3 space-y-2 border-t border-gray-700/50 pt-3">
                            <p className="text-xs font-medium text-gray-400">
                              Proposed Actions:
                            </p>
                            {msg.actions.map((action, i) => (
                              <div
                                key={`${msg.id}-action-${i}`}
                                className="flex items-center justify-between rounded-lg border border-gray-700 bg-gray-900/50 px-3 py-2"
                              >
                                <div className="flex-1">
                                  <span className="text-xs font-medium text-gray-200">
                                    {action.type === "pull_model"
                                      ? "Install"
                                      : action.type === "remove_model"
                                        ? "Remove"
                                        : "Restart"}
                                    {action.payload.model && (
                                      <code className="ml-1 rounded bg-gray-800 px-1 text-indigo-300">
                                        {action.payload.model}
                                      </code>
                                    )}
                                  </span>
                                  <span className="ml-2 text-xs text-gray-500">
                                    on {action.deviceName}
                                  </span>
                                </div>
                                <div className="flex gap-1">
                                  <button
                                    onClick={() => handleApproveAction(action)}
                                    className="flex items-center gap-1 rounded bg-emerald-600/20 px-2 py-1 text-xs text-emerald-400 hover:bg-emerald-600/30"
                                  >
                                    <CheckCircle size={12} />
                                    Approve
                                  </button>
                                  <button
                                    onClick={() => handleRejectAction(action)}
                                    className="flex items-center gap-1 rounded bg-red-600/20 px-2 py-1 text-xs text-red-400 hover:bg-red-600/30"
                                  >
                                    <XCircle size={12} />
                                    Reject
                                  </button>
                                </div>
                              </div>
                            ))}
                          </div>
                        )}
                      </div>
                    </div>
                  ))}
                  {sending && !streaming && (
                    <div className="flex justify-start">
                      <div className="flex items-center gap-2 rounded-lg bg-gray-800 px-4 py-3 text-sm text-gray-400">
                        <Loader2 className="h-4 w-4 animate-spin" />
                        Token is thinking...
                      </div>
                    </div>
                  )}
                  <div ref={messagesEndRef} />
                </div>
              )}
            </div>

            {/* Error banner */}
            {error && (
              <div className="mx-4 mb-2 rounded-lg border border-red-800/50 bg-red-900/20 px-3 py-2 text-xs text-red-400">
                {error}
                <button
                  onClick={() => setError("")}
                  className="ml-2 text-red-500 hover:text-red-300"
                >
                  Dismiss
                </button>
              </div>
            )}

            {/* Input */}
            <form onSubmit={sendMessage} className="border-t border-gray-800 p-4">
              <div className="flex items-end gap-2">
                <textarea
                  ref={inputRef}
                  value={input}
                  onChange={(e) => setInput(e.target.value)}
                  onKeyDown={handleKeyDown}
                  placeholder="Ask Token about models, hardware, fleet management..."
                  rows={1}
                  className="flex-1 resize-none rounded-lg border border-gray-700 bg-gray-800 px-4 py-3 text-sm text-gray-100 placeholder-gray-500 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
                  style={{ maxHeight: "120px" }}
                  onInput={(e) => {
                    const target = e.target as HTMLTextAreaElement;
                    target.style.height = "auto";
                    target.style.height = Math.min(target.scrollHeight, 120) + "px";
                  }}
                />
                <Button
                  type="submit"
                  variant="primary"
                  size="md"
                  disabled={sending || !input.trim() || input.length > 10_000}
                >
                  {sending ? (
                    <Loader2 className="h-4 w-4 animate-spin" />
                  ) : (
                    <Send size={16} />
                  )}
                </Button>
              </div>
              {input.length > 9000 && (
                <p className={`text-xs mt-1 ${input.length > 10_000 ? "text-red-400" : "text-yellow-500"}`}>
                  {input.length.toLocaleString()} / 10,000 characters
                </p>
              )}
            </form>
          </div>
        </Card>
      </div>
    </div>
  );
}
