"use client";

import { useState, useEffect, useRef, useCallback } from "react";
import type { ChatMessage, WSMessage, AgentStatus } from "@/lib/types";
import { STORAGE_KEYS } from "@/lib/constants";

export interface AgentStatusInfo {
  status: AgentStatus;
  toolName?: string;
}

interface UseChatReturn {
  messages: ChatMessage[];
  conversationId: string | null;
  connected: boolean;
  sending: boolean;
  agentStatus: AgentStatusInfo | null;
  send: (text: string) => Promise<void>;
}

function getStoredConversationId(agentName: string): string | null {
  if (typeof window === "undefined") return null;
  try {
    const stored = JSON.parse(
      localStorage.getItem(STORAGE_KEYS.CHAT_CONVERSATION) || "{}"
    );
    return stored[agentName] || null;
  } catch {
    return null;
  }
}

function saveConversationId(agentName: string, convId: string): void {
  try {
    const stored = JSON.parse(
      localStorage.getItem(STORAGE_KEYS.CHAT_CONVERSATION) || "{}"
    );
    stored[agentName] = convId;
    localStorage.setItem(STORAGE_KEYS.CHAT_CONVERSATION, JSON.stringify(stored));
  } catch {
    // localStorage unavailable
  }
}

export function useChat(agentName: string): UseChatReturn {
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [conversationId, setConversationId] = useState<string | null>(() =>
    getStoredConversationId(agentName)
  );
  const [connected, setConnected] = useState(false);
  const [sending, setSending] = useState(false);
  const [agentStatus, setAgentStatus] = useState<AgentStatusInfo | null>(null);
  const eventSourceRef = useRef<EventSource | null>(null);
  const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const statusClearTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Persist conversationId to localStorage when it changes
  useEffect(() => {
    if (conversationId) {
      saveConversationId(agentName, conversationId);
    }
  }, [conversationId, agentName]);

  // Load conversation history from backend on mount if we have a stored conversationId
  useEffect(() => {
    if (!conversationId) return;

    fetch(
      `/api/agents/${encodeURIComponent(agentName)}/history?conversation_id=${encodeURIComponent(conversationId)}`
    )
      .then((r) => {
        if (!r.ok) throw new Error("Not found");
        return r.json();
      })
      .then((data: { messages?: ChatMessage[] }) => {
        if (data.messages && data.messages.length > 0) {
          setMessages(data.messages);
        }
      })
      .catch(() => {
        // Conversation no longer exists on the server — clear stored id
        setConversationId(null);
        try {
          const stored = JSON.parse(
            localStorage.getItem(STORAGE_KEYS.CHAT_CONVERSATION) || "{}"
          );
          delete stored[agentName];
          localStorage.setItem(
            STORAGE_KEYS.CHAT_CONVERSATION,
            JSON.stringify(stored)
          );
        } catch {
          // ignore
        }
      });
    // Only run on mount (conversationId is initialized from localStorage)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [agentName]);

  const connectSSE = useCallback((convId: string) => {
    eventSourceRef.current?.close();
    if (reconnectTimer.current) clearTimeout(reconnectTimer.current);

    const es = new EventSource(
      `/api/agents/${encodeURIComponent(agentName)}/stream?conversation_id=${encodeURIComponent(convId)}`
    );

    es.onopen = () => setConnected(true);

    es.onmessage = (event) => {
      const data: WSMessage = JSON.parse(event.data);

      if (data.type === "reply" && data.conversation_id === convId) {
        const msg: ChatMessage = {
          id: crypto.randomUUID(),
          role: "assistant",
          text: data.text,
          timestamp: data.timestamp,
        };
        setMessages((prev) => [...prev, msg]);
        // Clear status when reply arrives
        setAgentStatus(null);
        if (statusClearTimer.current) clearTimeout(statusClearTimer.current);
      }

      if (data.type === "status" && data.status) {
        if (statusClearTimer.current) clearTimeout(statusClearTimer.current);

        if (data.status === "tool_done" || data.status === "idle") {
          // Brief flash then clear
          setAgentStatus({ status: data.status, toolName: data.tool_name });
          statusClearTimer.current = setTimeout(() => setAgentStatus(null), 1500);
        } else {
          setAgentStatus({ status: data.status, toolName: data.tool_name });
          // Auto-clear after 30s in case we miss a follow-up event
          statusClearTimer.current = setTimeout(() => setAgentStatus(null), 30000);
        }
      }
    };

    es.onerror = () => {
      setConnected(false);
      es.close();
      reconnectTimer.current = setTimeout(() => connectSSE(convId), 3000);
    };

    eventSourceRef.current = es;
  }, [agentName]);

  useEffect(() => {
    return () => {
      if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
      if (statusClearTimer.current) clearTimeout(statusClearTimer.current);
      eventSourceRef.current?.close();
    };
  }, []);

  useEffect(() => {
    if (conversationId) {
      connectSSE(conversationId);
    }
    return () => {
      if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
      eventSourceRef.current?.close();
      eventSourceRef.current = null;
    };
  }, [conversationId, connectSSE]);

  const send = useCallback(
    async (text: string) => {
      setSending(true);
      setAgentStatus({ status: "thinking" });
      try {
        const res = await fetch(`/api/agents/${encodeURIComponent(agentName)}/send`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ text, conversation_id: conversationId }),
        });

        if (!res.ok) throw new Error("Failed to send message");

        const data = await res.json();
        const convId = data.conversation_id as string;

        const userMsg: ChatMessage = {
          id: data.message_id as string,
          role: "user",
          text,
          timestamp: Date.now(),
        };
        setMessages((prev) => [...prev, userMsg]);

        if (!conversationId) {
          setConversationId(convId);
        }
      } finally {
        setSending(false);
      }
    },
    [conversationId, agentName]
  );

  return { messages, conversationId, connected, sending, agentStatus, send };
}
