"use client";

import { useCallback, useEffect, useRef, useImperativeHandle, forwardRef, useState } from "react";
import { useAuth } from "@/lib/auth/auth-context";
import { useChat } from "@/hooks/useChat";
import { useOrchestratorCommands } from "@/hooks/useOrchestratorCommands";
import { useWebSocket } from "@/hooks/useWebSocket";
import { useWorkspaceId } from "@/lib/hooks";
import { MessageList } from "./MessageList";
import { ChatInput, type ModelId, DEFAULT_TEMPERATURE, DEFAULT_MAX_TOKENS } from "./ChatInput";
import { ChatEmptyState } from "./ChatEmptyState";
import { AgentSelector } from "./AgentSelector";
import type { Message } from "@/hooks/useChat";

export interface ChatRef {
  loadConversation: (conversationId: string) => Promise<void>;
  startNewConversation: (projectId?: string | null) => void;
  getCurrentConversationId: () => string | null;
}

export interface NewConversationData {
  id: string;
  title: string | null;
  project_id: string | null;
  created_at: string;
  updated_at: string;
}

interface ChatProps {
  onConversationChange?: (
    conversationId: string | null,
    conversationData?: NewConversationData
  ) => void;
  onProjectChange?: () => void;
  initialProjectId?: string | null;
  onInitialProjectHandled?: () => void;
}

const WAITING_QUIPS = [
  "The AI is warming up... give it a moment.",
  "Loading the neural pathways...",
  "Waking up the LLM. It's not a morning model.",
  "Brewing some thoughts...",
  "The AI is stretching its parameters...",
  "Summoning intelligence from the void...",
  "Teaching electrons to think...",
  "Consulting the silicon oracle...",
  "The hamsters are spinning up the GPU...",
  "Defragmenting the neural networks...",
];

export const Chat = forwardRef<ChatRef, ChatProps>(function Chat(
  {
    onConversationChange,
    onProjectChange: _onProjectChange,
    initialProjectId,
    onInitialProjectHandled: _onInitialProjectHandled,
  },
  ref
) {
  void _onProjectChange;
  void _onInitialProjectHandled;

  const { user, isLoading: authLoading } = useAuth();

  // Model and params state — initialized from ChatInput's persisted values
  const [selectedModel, setSelectedModel] = useState<ModelId>("llama3.2");
  const [temperature, setTemperature] = useState<number>(DEFAULT_TEMPERATURE);
  const [maxTokens, setMaxTokens] = useState<number>(DEFAULT_MAX_TOKENS);
  const [selectedAgent, setSelectedAgent] = useState<string | null>(null);

  // Suggestion fill value: controls ChatInput's textarea content
  const [suggestionValue, setSuggestionValue] = useState<string | undefined>(undefined);

  const {
    messages,
    isLoading: isChatLoading,
    isStreaming,
    error,
    conversationId,
    conversationTitle,
    sendMessage,
    abortStream,
    loadConversation,
    startNewConversation,
    setMessages,
    clearError,
  } = useChat({
    model: selectedModel,
    temperature,
    maxTokens,
    ...(initialProjectId !== undefined && { projectId: initialProjectId }),
    ...(selectedAgent !== null && { agent: selectedAgent }),
  });

  // Read workspace ID from localStorage (set by auth-context after session check).
  // Cookie-based auth (withCredentials) handles authentication, so no explicit
  // token is needed here — pass an empty string as the token placeholder.
  const workspaceId = useWorkspaceId() ?? "";
  const { isConnected: isWsConnected } = useWebSocket(workspaceId, "", {});

  const { isCommand, executeCommand } = useOrchestratorCommands();

  const messagesEndRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLTextAreaElement>(null);
  const [loadingQuip, setLoadingQuip] = useState<string | null>(null);
  const quipTimerRef = useRef<NodeJS.Timeout | null>(null);
  const quipIntervalRef = useRef<NodeJS.Timeout | null>(null);

  // Identify the streaming message (last assistant message while streaming)
  const streamingMessageId =
    isStreaming && messages.length > 0 ? messages[messages.length - 1]?.id : undefined;

  // Whether the conversation is empty (only welcome message or no messages)
  const isEmptyConversation =
    messages.length === 0 ||
    (messages.length === 1 && messages[0]?.id === "welcome" && !isChatLoading && !isStreaming);

  useImperativeHandle(ref, () => ({
    loadConversation: async (cId: string): Promise<void> => {
      await loadConversation(cId);
    },
    startNewConversation: (projectId?: string | null): void => {
      startNewConversation(projectId);
    },
    getCurrentConversationId: (): string | null => conversationId,
  }));

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

  useEffect(() => {
    scrollToBottom();
  }, [messages, scrollToBottom]);

  useEffect(() => {
    if (conversationId && conversationTitle) {
      onConversationChange?.(conversationId, {
        id: conversationId,
        title: conversationTitle,
        project_id: initialProjectId ?? null,
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      });
    } else {
      onConversationChange?.(null);
    }
  }, [conversationId, conversationTitle, initialProjectId, onConversationChange]);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent): void => {
      // Cmd/Ctrl + / : Focus input
      if ((e.ctrlKey || e.metaKey) && e.key === "/") {
        e.preventDefault();
        inputRef.current?.focus();
      }
      // Cmd/Ctrl + N : Start new conversation
      if ((e.ctrlKey || e.metaKey) && (e.key === "n" || e.key === "N")) {
        e.preventDefault();
        startNewConversation(null);
        inputRef.current?.focus();
      }
      // Cmd/Ctrl + L : Clear / start new conversation
      if ((e.ctrlKey || e.metaKey) && (e.key === "l" || e.key === "L")) {
        e.preventDefault();
        startNewConversation(null);
        inputRef.current?.focus();
      }
    };
    document.addEventListener("keydown", handleKeyDown);
    return (): void => {
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [startNewConversation]);

  // Show loading quips only during non-streaming load (initial fetch wait)
  useEffect(() => {
    if (isChatLoading && !isStreaming) {
      quipTimerRef.current = setTimeout(() => {
        setLoadingQuip(WAITING_QUIPS[Math.floor(Math.random() * WAITING_QUIPS.length)] ?? null);
      }, 3000);

      quipIntervalRef.current = setInterval(() => {
        setLoadingQuip(WAITING_QUIPS[Math.floor(Math.random() * WAITING_QUIPS.length)] ?? null);
      }, 5000);
    } else {
      if (quipTimerRef.current) {
        clearTimeout(quipTimerRef.current);
        quipTimerRef.current = null;
      }
      if (quipIntervalRef.current) {
        clearInterval(quipIntervalRef.current);
        quipIntervalRef.current = null;
      }
      setLoadingQuip(null);
    }

    return (): void => {
      if (quipTimerRef.current) clearTimeout(quipTimerRef.current);
      if (quipIntervalRef.current) clearInterval(quipIntervalRef.current);
    };
  }, [isChatLoading, isStreaming]);

  const handleSendMessage = useCallback(
    async (content: string) => {
      if (isCommand(content)) {
        // Add user message immediately
        const userMessage: Message = {
          id: `user-${Date.now().toString()}-${Math.random().toString(36).slice(2, 8)}`,
          role: "user",
          content: content.trim(),
          createdAt: new Date().toISOString(),
        };
        setMessages((prev) => [...prev, userMessage]);

        // Execute orchestrator command
        const result = await executeCommand(content);
        if (result) {
          setMessages((prev) => [...prev, result]);
        }
        return;
      }

      await sendMessage(content);
    },
    [isCommand, executeCommand, setMessages, sendMessage]
  );

  const handleSuggestionClick = useCallback((prompt: string): void => {
    setSuggestionValue(prompt);
    // Clear after a tick so input receives it, then focus
    setTimeout(() => {
      inputRef.current?.focus();
    }, 0);
  }, []);

  if (authLoading) {
    return (
      <div
        className="flex flex-1 items-center justify-center"
        style={{ backgroundColor: "rgb(var(--color-background))" }}
      >
        <div className="flex items-center gap-3">
          <div
            className="h-5 w-5 animate-spin rounded-full border-2 border-t-transparent"
            style={{ borderColor: "rgb(var(--accent-primary))", borderTopColor: "transparent" }}
          />
          <span style={{ color: "rgb(var(--text-secondary))" }}>Loading...</span>
        </div>
      </div>
    );
  }

  return (
    <div
      className="flex flex-1 flex-col"
      style={{ backgroundColor: "rgb(var(--color-background))" }}
    >
      {/* Connection Status Indicator */}
      {user && !isWsConnected && (
        <div
          className="border-b px-4 py-2"
          style={{
            backgroundColor: "rgb(var(--surface-0))",
            borderColor: "rgb(var(--border-default))",
          }}
        >
          <div className="flex items-center gap-2">
            <div
              className="h-2 w-2 rounded-full"
              style={{ backgroundColor: "rgb(var(--semantic-warning))" }}
            />
            <span className="text-sm" style={{ color: "rgb(var(--text-secondary))" }}>
              Reconnecting to server...
            </span>
          </div>
        </div>
      )}

      {/* Messages Area */}
      <div className="flex-1 overflow-y-auto">
        <div className="mx-auto max-w-4xl px-4 py-6 lg:px-8">
          {isEmptyConversation ? (
            <ChatEmptyState onSuggestionClick={handleSuggestionClick} />
          ) : (
            <MessageList
              messages={messages as (Message & { thinking?: string })[]}
              isLoading={isChatLoading}
              isStreaming={isStreaming}
              {...(streamingMessageId != null ? { streamingMessageId } : {})}
              loadingQuip={loadingQuip}
            />
          )}
          <div ref={messagesEndRef} />
        </div>
      </div>

      {/* Error Alert */}
      {error && (
        <div className="mx-4 mb-2 lg:mx-auto lg:max-w-4xl lg:px-8">
          <div
            className="flex items-center justify-between rounded-lg border px-4 py-3"
            style={{
              backgroundColor: "rgb(var(--semantic-error-light))",
              borderColor: "rgb(var(--semantic-error) / 0.3)",
            }}
            role="alert"
          >
            <div className="flex items-center gap-3">
              <svg
                className="h-4 w-4 flex-shrink-0"
                style={{ color: "rgb(var(--semantic-error))" }}
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2}
              >
                <circle cx="12" cy="12" r="10" />
                <line x1="12" y1="8" x2="12" y2="12" />
                <line x1="12" y1="16" x2="12.01" y2="16" />
              </svg>
              <span className="text-sm" style={{ color: "rgb(var(--semantic-error-dark))" }}>
                {error}
              </span>
            </div>
            <button
              onClick={clearError}
              className="rounded p-1 transition-colors hover:bg-black/5"
              aria-label="Dismiss error"
            >
              <svg
                className="h-4 w-4"
                style={{ color: "rgb(var(--semantic-error))" }}
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
                strokeWidth={2}
              >
                <path d="M18 6 6 18M6 6l12 12" />
              </svg>
            </button>
          </div>
        </div>
      )}

      {/* Input Area */}
      {!user && (
        <div className="mx-4 mb-2 lg:mx-auto lg:max-w-4xl lg:px-8">
          <div
            className="flex items-center justify-center gap-2 rounded-lg border px-4 py-3 text-center"
            style={{
              backgroundColor: "rgb(var(--surface-1))",
              borderColor: "rgb(var(--border-default))",
            }}
          >
            <svg
              className="h-4 w-4"
              style={{ color: "rgb(var(--text-secondary))" }}
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
              strokeWidth={2}
            >
              <path d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" />
            </svg>
            <span className="text-sm" style={{ color: "rgb(var(--text-secondary))" }}>
              Sign in to chat with Jarvis
            </span>
          </div>
        </div>
      )}
      <div
        className="sticky bottom-0 border-t"
        style={{
          backgroundColor: "rgb(var(--surface-0))",
          borderColor: "rgb(var(--border-default))",
        }}
      >
        <div className="mx-auto max-w-4xl px-4 py-4 lg:px-8">
          <div className="mb-3">
            <AgentSelector
              selectedAgent={selectedAgent}
              onChange={setSelectedAgent}
              disabled={isChatLoading || isStreaming || !user}
            />
          </div>
          <ChatInput
            onSend={handleSendMessage}
            disabled={isChatLoading || !user}
            inputRef={inputRef}
            isStreaming={isStreaming}
            onStopStreaming={abortStream}
            onModelChange={setSelectedModel}
            onTemperatureChange={setTemperature}
            onMaxTokensChange={setMaxTokens}
            {...(suggestionValue !== undefined ? { externalValue: suggestionValue } : {})}
          />
        </div>
      </div>
    </div>
  );
});
