"use client";

import React, {
  useCallback,
  useEffect,
  useRef,
  useState,
} from "react";
import { Box, Drawer, alpha, useTheme } from "@mui/material";
import { useNotification } from "@refinedev/core";

import { getAccessToken } from "@/lib/authToken";
import { fetchAgentModels, type AgentModelOption } from "@/lib/chatModels";
import type { AgentApprovalMode, AgentModel } from "@/lib/chatStream";
import { useProjectStore } from "@/store/projectStore";
import { AgentComposer, type AgentComposerHandle } from "./AgentComposer";
import { AgentHeader } from "./AgentHeader";
import { AgentHistoryPanel } from "./AgentHistoryPanel";
import { AgentWorkspace } from "./AgentWorkspace";
import { Blob } from "./GlobalChatboxParts";
import type { Props } from "./GlobalChatbox.types";
import { PRESET_PROMPTS } from "./globalChatboxUtils";
import { useSpeechRecognition, useSpeechSynthesis } from "./globalChatboxVoice";
import { useAgentChatSession } from "./hooks/useAgentChatSession";
import { useAgentToolActions } from "./hooks/useAgentToolActions";

const STREAMING_BOTTOM_RESERVE_PX = 180;
const STREAMING_SCROLL_RESTORE_AT_PX = STREAMING_BOTTOM_RESERVE_PX - 36;

export const GlobalChatbox: React.FC<Props> = ({ open, onClose }) => {
  const [width, setWidth] = useState(520);
  const [isResizing, setIsResizing] = useState(false);
  const [isHistoryOpen, setIsHistoryOpen] = useState(false);
  const [isCheckingAuth, setIsCheckingAuth] = useState(false);
  const [modelOptions, setModelOptions] = useState<AgentModelOption[]>([]);
  const [selectedModel, setSelectedModel] = useState<AgentModel | undefined>(undefined);
  const [approvalMode, setApprovalMode] =
    useState<AgentApprovalMode>("request");

  const bottomRef = useRef<HTMLDivElement>(null);
  const workspaceScrollRef = useRef<HTMLDivElement>(null);
  const isNearBottomRef = useRef(true);
  const streamingScrollFrameRef = useRef<number | null>(null);
  const composerRef = useRef<AgentComposerHandle | null>(null);
  const initializedProjectIdRef = useRef<string | null | undefined>(undefined);
  const theme = useTheme();
  const { open: openNotification } = useNotification();
  const currentProjectId = useProjectStore((state) => state.currentProjectId);

  const {
    speechState,
    speakingMessageId,
    speak: handleSpeak,
    pause: handlePauseSpeech,
    resume: handleResumeSpeech,
    stop: handleStopSpeech,
    isSupported: isTtsSupported,
  } = useSpeechSynthesis();

  const handleSpeechResult = useCallback((text: string) => {
    composerRef.current?.append(text);
  }, []);

  const {
    isListening,
    start: startListening,
    stop: stopListening,
    isSupported: isSttSupported,
  } = useSpeechRecognition(handleSpeechResult);

  useEffect(() => {
    let cancelled = false;

    const loadModels = async () => {
      try {
        const modelConfig = await fetchAgentModels();
        if (cancelled) return;
        setModelOptions(modelConfig.models);
        setSelectedModel((current) => {
          if (current && modelConfig.models.some((model) => model.id === current)) {
            return current;
          }
          return modelConfig.defaultModel;
        });
      } catch (error) {
        console.error("[GlobalChatbox] Failed to load agent models:", error);
        if (!cancelled) {
          setModelOptions([]);
          setSelectedModel(undefined);
        }
      }
    };

    void loadModels();

    return () => {
      cancelled = true;
    };
  }, []);

  const handleToolCall = useAgentToolActions();
  const {
    messages,
    chatSessions,
    activeSessionId,
    isHydrating,
    loadingSessionId,
    isStreaming,
    sessionTitle,
    sendPrompt,
    createBranch,
    abort,
    replyPermission,
    replyQuestion,
    rejectQuestion,
    createSession,
    renameSession,
    removeSession,
    switchSession,
  } = useAgentChatSession({
    projectId: currentProjectId,
    onToolCall: handleToolCall,
    onBeforeSend: stopListening,
    getModel: () => selectedModel,
    getApprovalMode: () => approvalMode,
  });

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

  const cancelStreamingScroll = useCallback(() => {
    if (streamingScrollFrameRef.current === null) return;
    window.cancelAnimationFrame(streamingScrollFrameRef.current);
    streamingScrollFrameRef.current = null;
  }, []);

  const scheduleStreamingScrollToBottom = useCallback(() => {
    if (streamingScrollFrameRef.current !== null) return;
    streamingScrollFrameRef.current = window.requestAnimationFrame(() => {
      streamingScrollFrameRef.current = null;
      const container = workspaceScrollRef.current;
      if (!container || !isNearBottomRef.current) return;

      const distanceToBottom =
        container.scrollHeight - container.scrollTop - container.clientHeight;
      if (distanceToBottom < STREAMING_SCROLL_RESTORE_AT_PX) return;

      container.scrollTop = container.scrollHeight - container.clientHeight;
    });
  }, []);

  const handleWorkspaceScrollStateChange = useCallback((isNearBottom: boolean) => {
    isNearBottomRef.current = isNearBottom;
  }, []);

  const resetConversationView = useCallback(() => {
    composerRef.current?.clear();
    setIsHistoryOpen(false);
    window.setTimeout(() => {
      composerRef.current?.focus();
      isNearBottomRef.current = true;
      cancelStreamingScroll();
      scrollToBottom("auto");
    }, 0);
  }, [cancelStreamingScroll, scrollToBottom]);

  useEffect(() => {
    if (isStreaming) {
      if (!isNearBottomRef.current) return;
      scheduleStreamingScrollToBottom();
      return;
    }
    cancelStreamingScroll();
    scrollToBottom("smooth");
  }, [
    cancelStreamingScroll,
    isStreaming,
    messages,
    scheduleStreamingScrollToBottom,
    scrollToBottom,
  ]);

  useEffect(
    () => () => {
      cancelStreamingScroll();
    },
    [cancelStreamingScroll],
  );

  useEffect(() => {
    if (
      !open ||
      isHydrating ||
      initializedProjectIdRef.current === currentProjectId
    ) {
      return;
    }

    initializedProjectIdRef.current = currentProjectId;
    createSession();
    resetConversationView();
  }, [createSession, currentProjectId, isHydrating, open, resetConversationView]);

  const handleSend = useCallback(async (prompt: string) => {
    if (isStreaming || isCheckingAuth) return;

    setIsCheckingAuth(true);
    try {
      const accessToken = await getAccessToken();
      if (!accessToken) {
        composerRef.current?.setValue(prompt);
        openNotification?.({
          type: "error",
          message: "登录状态已失效",
          description: "请重新登录后再发送对话。",
        });
        return;
      }

      void sendPrompt(prompt);
    } catch (error) {
      composerRef.current?.setValue(prompt);
      openNotification?.({
        type: "error",
        message: "登录状态校验失败",
        description: error instanceof Error ? error.message : "请重新登录后再试。",
      });
    } finally {
      setIsCheckingAuth(false);
    }
  }, [isCheckingAuth, isStreaming, openNotification, sendPrompt]);

  const handleNewConversation = useCallback(() => {
    handleStopSpeech();
    stopListening();
    createSession();
    resetConversationView();
  }, [createSession, handleStopSpeech, resetConversationView, stopListening]);

  const handleHistoryToggle = useCallback(() => {
    setIsHistoryOpen((prev) => !prev);
  }, []);

  const handleSelectSession = useCallback(
    (sessionId: string, title: string) => {
      composerRef.current?.clear();
      void switchSession(sessionId, title);
    },
    [switchSession],
  );

  const handleDeleteSession = useCallback(
    (sessionId: string) => {
      void removeSession(sessionId);
    },
    [removeSession],
  );

  const handleRenameSession = useCallback(
    (sessionId: string, title: string) => {
      void renameSession(sessionId, title);
    },
    [renameSession],
  );

  const handleRenameActiveSession = useCallback(
    (title: string) => {
      if (!activeSessionId) return;
      void renameSession(activeSessionId, title);
    },
    [activeSessionId, renameSession],
  );

  const handleMouseDown = useCallback((event: React.MouseEvent) => {
    event.preventDefault();
    setIsResizing(true);
  }, []);

  useEffect(() => {
    const handleMouseMove = (event: MouseEvent) => {
      if (!isResizing) return;
      const newWidth = window.innerWidth - event.clientX;
      if (newWidth > 360 && newWidth < 800) {
        setWidth(newWidth);
      }
    };

    const handleMouseUp = () => {
      setIsResizing(false);
    };

    if (isResizing) {
      window.addEventListener("mousemove", handleMouseMove);
      window.addEventListener("mouseup", handleMouseUp);
    }

    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("mouseup", handleMouseUp);
    };
  }, [isResizing]);

  return (
    <Drawer
      anchor="right"
      variant="temporary"
      open={open}
      onClose={onClose}
      hideBackdrop
      disableScrollLock
      disableEnforceFocus
      ModalProps={{ keepMounted: true }}
      sx={{
        zIndex: (muiTheme) => muiTheme.zIndex.modal + 100,
        pointerEvents: "none",
      }}
      PaperProps={{
        sx: {
          width: { xs: "100%", sm: width },
          background: "transparent",
          boxShadow: "none",
          overflow: open ? "visible" : "hidden",
          zIndex: (muiTheme) => muiTheme.zIndex.modal + 100,
          pointerEvents: "auto",
          transition: isResizing ? "none" : undefined,
        },
      }}
    >
      <Box
        sx={{
          height: "100%",
          display: "flex",
          flexDirection: "column",
          bgcolor: alpha("#fff", 0.76),
          backdropFilter: "blur(30px)",
          position: "relative",
        }}
      >
        <Box
          onMouseDown={handleMouseDown}
          sx={{
            position: "absolute",
            left: 0,
            top: 0,
            bottom: 0,
            width: "6px",
            cursor: "col-resize",
            zIndex: 200,
            "&:hover": {
              bgcolor: alpha(theme.palette.primary.main, 0.2),
            },
            "&::after": {
              content: '""',
              position: "absolute",
              left: "50%",
              top: "50%",
              transform: "translate(-50%, -50%)",
              width: "2px",
              height: "40px",
              bgcolor: alpha(theme.palette.divider, 0.4),
              borderRadius: "1px",
            },
          }}
        />

        <Blob color={alpha(theme.palette.primary.main, 0.28)} size={300} top="-10%" left="-20%" delay={0} />
        <Blob color={alpha(theme.palette.secondary.main, 0.24)} size={250} top="40%" left="60%" delay={2} />
        <Blob color={alpha(theme.palette.success.light, 0.18)} size={200} top="80%" left="-10%" delay={4} />

        <AgentHeader
          sessionTitle={sessionTitle}
          canRenameSessionTitle={Boolean(activeSessionId)}
          isHydrating={isHydrating}
          isStreaming={isStreaming}
          isHistoryOpen={isHistoryOpen}
          onHistoryToggle={handleHistoryToggle}
          onRenameSessionTitle={handleRenameActiveSession}
          onNewConversation={handleNewConversation}
          onClose={onClose}
        />

        <Box sx={{ flex: 1, display: "flex", minHeight: 0, position: "relative", overflow: "hidden" }}>
          <Box
            onClick={() => setIsHistoryOpen(false)}
            sx={{
              position: "absolute",
              inset: 0,
              bgcolor: alpha("#000", 0.05),
              backdropFilter: "blur(2px)",
              opacity: isHistoryOpen ? 1 : 0,
              pointerEvents: isHistoryOpen ? "auto" : "none",
              transition: "opacity 0.3s ease",
              zIndex: 10,
            }}
          />
          <Box
            sx={{
              position: "absolute",
              top: 0,
              bottom: 0,
              left: 0,
              width: 268,
              zIndex: 20,
              transform: isHistoryOpen ? "translateX(0)" : "translateX(-100%)",
              transition: "transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1)",
              boxShadow: isHistoryOpen ? `4px 0 24px ${alpha("#000", 0.08)}` : "none",
            }}
          >
            <AgentHistoryPanel
              sessions={chatSessions}
              activeSessionId={activeSessionId}
              isHydrating={isHydrating}
              isLoadingSessions={isHydrating && chatSessions.length === 0}
              loadingSessionId={loadingSessionId}
              onNewSession={() => {
                handleNewConversation();
                setIsHistoryOpen(false);
              }}
              onSelectSession={(id, title) => {
                handleSelectSession(id, title);
                setIsHistoryOpen(false);
              }}
              onRenameSession={handleRenameSession}
              onDeleteSession={handleDeleteSession}
            />
          </Box>

          <Box sx={{ flex: 1, display: "flex", minWidth: 0, flexDirection: "column" }}>
            <AgentWorkspace
              messages={messages}
              isStreaming={isStreaming}
              isLoadingSession={Boolean(loadingSessionId)}
              scrollContainerRef={workspaceScrollRef}
              bottomRef={bottomRef}
              onScrollStateChange={handleWorkspaceScrollStateChange}
              speakingMessageId={speakingMessageId}
              speechState={speechState}
              onSpeak={handleSpeak}
              onPauseSpeech={handlePauseSpeech}
              onResumeSpeech={handleResumeSpeech}
              onStopSpeech={handleStopSpeech}
              isTtsSupported={isTtsSupported}
              onCreateBranch={createBranch}
              onReplyPermission={replyPermission}
              onReplyQuestion={replyQuestion}
              onRejectQuestion={rejectQuestion}
            />

            <AgentComposer
              ref={composerRef}
              isHydrating={isHydrating || isCheckingAuth}
              isStreaming={isStreaming}
              isListening={isListening}
              isSttSupported={isSttSupported}
              presets={PRESET_PROMPTS}
              onSend={handleSend}
              onAbort={abort}
              onStartListening={startListening}
              onStopListening={stopListening}
              modelOptions={modelOptions}
              selectedModel={selectedModel}
              onModelChange={setSelectedModel}
              approvalMode={approvalMode}
              onApprovalModeChange={setApprovalMode}
            />
          </Box>
        </Box>
      </Box>
    </Drawer>
  );
};
