"use client";

import { useState, useCallback, useRef } from "react";
import { ChatMessage, WorkflowOps } from "@/lib/types";
import { generateId } from "@/lib/utils";

interface UseChatOptions {
  channelId: string;
}

interface UseChatReturn {
  messages: ChatMessage[];
  isLoading: boolean;
  sendMessage: (content: string) => Promise<void>;
  clearMessages: () => void;
  addMessage: (msg: ChatMessage) => void;
  updateMessage: (id: string, updates: Partial<ChatMessage>) => void;
  removeMessage: (id: string) => void;
}

// Global message store - persists across channel switches
const messageStore = new Map<string, ChatMessage[]>();

export function useChat({ channelId }: UseChatOptions): UseChatReturn {
  const [messages, setMessages] = useState<ChatMessage[]>(
    () => messageStore.get(channelId) || []
  );
  const [isLoading, setIsLoading] = useState(false);
  const abortControllerRef = useRef<AbortController | null>(null);
  const currentChannelRef = useRef(channelId);

  // Sync messages to store whenever they change
  const updateMessages = useCallback(
    (updater: ChatMessage[] | ((prev: ChatMessage[]) => ChatMessage[])) => {
      setMessages((prev) => {
        const next =
          typeof updater === "function" ? updater(prev) : updater;
        messageStore.set(channelId, next);
        return next;
      });
    },
    [channelId]
  );

  // Reload messages when channel changes
  if (currentChannelRef.current !== channelId) {
    currentChannelRef.current = channelId;
    const stored = messageStore.get(channelId) || [];
    if (stored !== messages) {
      setMessages(stored);
    }
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
      abortControllerRef.current = null;
      setIsLoading(false);
    }
  }

  const sendMessage = useCallback(
    async (content: string) => {
      if (!content.trim() || isLoading) return;

      // Cancel any pending request
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }

      const userMessage: ChatMessage = {
        id: generateId(),
        role: "user",
        content: content.trim(),
        timestamp: new Date(),
      };

      const assistantMessage: ChatMessage = {
        id: generateId(),
        role: "assistant",
        content: "",
        timestamp: new Date(),
      };

      updateMessages((prev) => [...prev, userMessage, assistantMessage]);
      setIsLoading(true);

      const abortController = new AbortController();
      abortControllerRef.current = abortController;

      try {
        const allMessages = [
          ...(messageStore.get(channelId) || []).slice(0, -1), // exclude empty assistant msg
        ];

        const apiMessages = allMessages.map((m) => ({
          role: m.role,
          content: m.content,
        }));

        const response = await fetch("/api/chat", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            messages: apiMessages,
            channelId,
          }),
          signal: abortController.signal,
        });

        if (!response.ok) {
          throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }

        const reader = response.body?.getReader();
        if (!reader) throw new Error("No response body");

        const decoder = new TextDecoder();
        let buffer = "";
        let textContent = "";
        let mcpApps: any[] | null = null;
        let parsedMetadata = false;

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

          const chunk = decoder.decode(value, { stream: true });
          
          if (!parsedMetadata) {
            // Still looking for metadata in first line
            buffer += chunk;
            
            if (buffer.includes("\n")) {
              // We have a complete first line
              const newlineIndex = buffer.indexOf("\n");
              const firstLine = buffer.substring(0, newlineIndex);
              
              try {
                const parsed = JSON.parse(firstLine);
                if (parsed.type === "mcp_apps" && parsed.data) {
                  // Found MCP apps metadata
                  mcpApps = parsed.data;
                  // Text content starts after the newline
                  textContent = buffer.substring(newlineIndex + 1);
                } else {
                  // First line is not metadata JSON
                  textContent = buffer;
                }
              } catch {
                // Not valid JSON, treat buffer as text
                textContent = buffer;
              }
              
              parsedMetadata = true;
              buffer = "";
            }
          } else {
            // Metadata already parsed, append chunk to text
            textContent += chunk;
          }

          // Parse workflow JSON and update the assistant message
          let displayContent = parsedMetadata ? textContent : "";
          let workflowOps: WorkflowOps | undefined = undefined;

          if (parsedMetadata && textContent) {
            // Check for complete <!--WORKFLOW_JSON:{...}:END_WORKFLOW--> block
            const completeMatch = textContent.match(
              /<!--WORKFLOW_JSON:([\s\S]*?):END_WORKFLOW-->/
            );
            if (completeMatch) {
              try {
                workflowOps = JSON.parse(completeMatch[1]) as WorkflowOps;
              } catch {
                // JSON parse failed — ignore
              }
              // Strip the block from displayed text either way
              displayContent = textContent
                .replace(/<!--WORKFLOW_JSON:[\s\S]*?:END_WORKFLOW-->/, "")
                .trim();
            } else {
              // Check for incomplete marker (still streaming)
              const startIdx = textContent.indexOf("<!--WORKFLOW_JSON:");
              if (startIdx !== -1) {
                displayContent = textContent.substring(0, startIdx).trim();
              }
            }
          }

          updateMessages((prev) => {
            const updated = [...prev];
            const lastMsg = updated[updated.length - 1];
            if (lastMsg && lastMsg.role === "assistant") {
              updated[updated.length - 1] = {
                ...lastMsg,
                content: displayContent,
                mcpApps: mcpApps || undefined,
                workflowOps,
              };
            }
            return updated;
          });
        }
      } catch (error: unknown) {
        if (error instanceof Error && error.name === "AbortError") return;

        const errorMsg =
          error instanceof Error ? error.message : "An error occurred";
        updateMessages((prev) => {
          const updated = [...prev];
          const lastMsg = updated[updated.length - 1];
          if (lastMsg && lastMsg.role === "assistant") {
            updated[updated.length - 1] = {
              ...lastMsg,
              content: `⚠️ Error: ${errorMsg}\n\nPlease try again.`,
            };
          }
          return updated;
        });
      } finally {
        setIsLoading(false);
        abortControllerRef.current = null;
      }
    },
    [channelId, isLoading, updateMessages]
  );

  const clearMessages = useCallback(() => {
    updateMessages([]);
  }, [updateMessages]);

  const addMessage = useCallback(
    (msg: ChatMessage) => {
      updateMessages((prev) => [...prev, msg]);
    },
    [updateMessages]
  );

  const updateMessage = useCallback(
    (id: string, updates: Partial<ChatMessage>) => {
      updateMessages((prev) =>
        prev.map((m) => (m.id === id ? { ...m, ...updates } : m))
      );
    },
    [updateMessages]
  );

  const removeMessage = useCallback(
    (id: string) => {
      updateMessages((prev) => prev.filter((m) => m.id !== id));
    },
    [updateMessages]
  );

  return {
    messages,
    isLoading,
    sendMessage,
    clearMessages,
    addMessage,
    updateMessage,
    removeMessage,
  };
}
