import { useChat } from "@ai-sdk/react";
import { CopyOutlined, EditOutlined, RedoOutlined, RobotOutlined } from "@ant-design/icons";
import { Sender } from "@ant-design/x";
import { useQueryClient } from "@tanstack/react-query";
import { DefaultChatTransport, type UIMessage } from "ai";
import { App, Button, Card, Divider, Flex, Input, Select, Spin, Typography } from "antd";
import { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";

import {
  createConversation,
  fetchConversation,
  fetchMessages,
  updateConversation,
} from "../../shared/hooks/use-conversations";
import { useLogger } from "../../shared/hooks/use-logger";
import { ChatScrollArea } from "./ChatScrollArea";
import { ReasoningPart } from "./parts/ReasoningPart";
import { TextPart } from "./parts/TextPart";
import { ToolPart } from "./parts/ToolPart";

interface ChatPanelProps {
  conversationId: null | string;
  defaultModelId: null | string;
  onConversationCreated: (id: string) => void;
  projectId: string;
  textModels: Array<{ id: string; name: string }>;
}

export function ChatPanel({
  conversationId,
  defaultModelId: _defaultModelId,
  onConversationCreated,
  projectId,
  textModels,
}: ChatPanelProps) {
  const { message } = App.useApp();
  const logger = useLogger({ component: "ChatPanel", page: "workbench" });
  const queryClient = useQueryClient();
  const [input, setInput] = useState("");
  const [editingMessageId, setEditingMessageId] = useState<null | string>(null);
  const [editText, setEditText] = useState("");
  const [loadingHistory, setLoadingHistory] = useState(false);
  const [selectedModelId, setSelectedModelId] = useState<null | string>(null);
  const fetchRef = useRef(fetchMessages);
  const skipHistoryLoadRef = useRef<null | string>(null);

  const modelOptions = useMemo(() => textModels.map((m) => ({ label: m.name, value: m.id })), [textModels]);

  const { messages, regenerate, sendMessage, setMessages, status, stop } = useChat({
    onError: (err) => {
      logger.error("聊天发送失败", { error: err.message });
      void message.error(`发送失败：${err.message}`);
    },
    transport: new DefaultChatTransport({ api: `/api/projects/${projectId}/chat` }),
  });

  const isLoading = status === "submitted" || status === "streaming";

  useEffect(() => {
    if (!conversationId) {
      setMessages([]);
      return;
    }

    if (skipHistoryLoadRef.current === conversationId) {
      skipHistoryLoadRef.current = null;
      return;
    }

    let cancelled = false;

    const load = async () => {
      setLoadingHistory(true);
      setInput("");
      setMessages([]);
      try {
        const msgPromise = fetchRef.current(projectId, conversationId);

        const data = await msgPromise;
        if (cancelled) return;

        const history = data.items
          .filter((m: { role: string }) => m.role === "user" || m.role === "assistant")
          .reverse()
          .map((m: { content: string; id: string; parts: null | string; role: string }) => ({
            id: m.id,
            parts: m.parts ? (JSON.parse(m.parts) as UIMessage["parts"]) : [{ text: m.content, type: "text" as const }],
            role: m.role as "assistant" | "user",
          }));
        setMessages(history);
      } catch (err: unknown) {
        if (!cancelled) {
          const msg = err instanceof Error ? err.message : String(err);
          logger.error("加载历史失败", { conversationId, error: msg, projectId });
          void message.error(`加载历史失败：${msg}`);
        }
      } finally {
        if (!cancelled) setLoadingHistory(false);
      }
    };

    void load();

    return () => {
      cancelled = true;
    };
  }, [conversationId, projectId, setMessages, message, logger]);

  useEffect(() => {
    if (!conversationId) return;
    const firstTextId = textModels[0]?.id;
    if (!firstTextId) return;

    void fetchConversation(projectId, conversationId)
      .then((conv) => {
        if (textModels.every((m) => m.id !== conv.modelId)) {
          setSelectedModelId(firstTextId);
          void updateConversation(projectId, conversationId, { modelId: firstTextId });
        } else {
          setSelectedModelId(conv.modelId);
        }
      })
      .catch((err: unknown) => {
        const msg = err instanceof Error ? err.message : String(err);
        logger.warn("获取会话模型信息失败", { conversationId, error: msg, projectId });
      });
  }, [conversationId, textModels, projectId, logger]);

  useEffect(() => {
    if (status === "ready" && conversationId) {
      void queryClient.invalidateQueries({ queryKey: ["conversations", projectId] });
    }
  }, [status, conversationId, projectId, queryClient]);

  const displayModelId = conversationId ? selectedModelId : (textModels[0]?.id ?? null);

  const handleModelChange = useCallback(
    (value: string) => {
      setSelectedModelId(value);
      if (conversationId) {
        void updateConversation(projectId, conversationId, { modelId: value }).catch((err: unknown) => {
          const msg = err instanceof Error ? err.message : String(err);
          logger.warn("更新会话模型失败", { conversationId, error: msg, projectId });
        });
      }
    },
    [projectId, conversationId, logger],
  );

  const handleSenderSubmit = useCallback(
    (msg: string) => {
      if (!msg.trim()) return;

      if (!conversationId) {
        void (async () => {
          try {
            const conv = await createConversation(projectId, displayModelId ?? undefined);
            skipHistoryLoadRef.current = conv.id;
            void queryClient.invalidateQueries({ queryKey: ["conversations", projectId] });
            void sendMessage({ text: msg }, { body: { conversationId: conv.id } });
            setInput("");
            onConversationCreated(conv.id);
          } catch (err: unknown) {
            setInput(msg);
            const errMsg = err instanceof Error ? err.message : String(err);
            logger.error("创建会话失败", { error: errMsg, projectId });
            void message.error(`创建会话失败：${errMsg}`);
          }
        })();
        return;
      }

      setInput("");
      void sendMessage({ text: msg }, { body: { conversationId } }).catch((err: unknown) => {
        const errMsg = err instanceof Error ? err.message : String(err);
        logger.error("发送消息失败", { conversationId, error: errMsg, projectId });
      });
    },
    [sendMessage, conversationId, projectId, onConversationCreated, message, queryClient, displayModelId, logger],
  );

  const renderSenderFooter = useCallback(
    (actionNode: ReactNode) => (
      <Flex align="center" justify="space-between">
        <Select
          className="chat-model-select"
          disabled={isLoading}
          onChange={handleModelChange}
          options={modelOptions}
          placeholder="选择模型"
          value={displayModelId}
        />
        <Flex align="center">
          <Divider orientation="vertical" />
          {actionNode}
        </Flex>
      </Flex>
    ),
    [isLoading, handleModelChange, modelOptions, displayModelId],
  );

  const handleStop = useCallback(() => {
    void stop().catch((err: unknown) => {
      const msg = err instanceof Error ? err.message : String(err);
      logger.warn("停止聊天失败", { error: msg });
    });
  }, [stop, logger]);

  const extractText = useCallback((msg: UIMessage) => {
    return msg.parts
      .filter((p) => p.type === "text")
      .map((p) => (p as { text: string; type: "text" }).text)
      .join("");
  }, []);

  const handleCopy = useCallback(
    (msg: UIMessage) => {
      const text = extractText(msg);
      void navigator.clipboard
        .writeText(text)
        .then(() => {
          void message.success("已复制");
        })
        .catch((err: unknown) => {
          const msg = err instanceof Error ? err.message : String(err);
          logger.warn("复制失败", { error: msg });
        });
    },
    [extractText, message, logger],
  );

  const handleEditStart = useCallback(
    (msg: UIMessage) => {
      setEditingMessageId(msg.id);
      setEditText(extractText(msg));
    },
    [extractText],
  );

  const handleEditConfirm = useCallback(() => {
    if (!editText.trim() || !conversationId) return;
    setEditingMessageId(null);
    const idx = messages.findIndex((m) => m.id === editingMessageId);
    if (idx === -1) return;
    setMessages(messages.slice(0, idx));
    void sendMessage({ text: editText }, { body: { conversationId } }).catch((err: unknown) => {
      const msg = err instanceof Error ? err.message : String(err);
      logger.error("重新发送消息失败", { conversationId, error: msg, projectId });
    });
  }, [editText, conversationId, messages, editingMessageId, setMessages, sendMessage, logger, projectId]);

  const handleEditCancel = useCallback(() => {
    setEditingMessageId(null);
    setEditText("");
  }, []);

  const handleRegenerate = useCallback(() => {
    if (!conversationId) return;
    void regenerate({ body: { conversationId } }).catch((err: unknown) => {
      const msg = err instanceof Error ? err.message : String(err);
      logger.error("重新生成失败", { conversationId, error: msg, projectId });
    });
  }, [regenerate, conversationId, logger, projectId]);

  const getCardExtra = useCallback(
    (msg: UIMessage, idx: number) => {
      const isLast = idx === messages.length - 1;
      const lastUserIdx = messages.findLastIndex((m) => m.role === "user");
      const isLastUser = lastUserIdx >= 0 && idx === lastUserIdx;
      const isLastAssistant = isLast && msg.role === "assistant";
      const isEditing = editingMessageId === msg.id;

      if (isLoading) return null;

      const buttons: React.ReactNode[] = [];

      buttons.push(
        <Button
          className="btn-dimmed"
          icon={<CopyOutlined />}
          key="copy"
          onClick={() => handleCopy(msg)}
          size="small"
          type="text"
        />,
      );

      if (isLastUser && !isEditing) {
        buttons.push(
          <Button
            className="btn-dimmed"
            icon={<EditOutlined />}
            key="edit"
            onClick={() => handleEditStart(msg)}
            size="small"
            type="text"
          />,
        );
      }

      if (isLastAssistant) {
        buttons.push(
          <Button
            className="btn-dimmed"
            icon={<RedoOutlined />}
            key="regenerate"
            onClick={handleRegenerate}
            size="small"
            type="text"
          />,
        );
      }

      return <Flex gap={4}>{buttons}</Flex>;
    },
    [messages, isLoading, editingMessageId, handleCopy, handleEditStart, handleRegenerate],
  );

  if (!conversationId) {
    return (
      <div className="app-chat-panel">
        <div className="chat-welcome-area">
          <Flex align="center" gap={12} vertical>
            <RobotOutlined className="welcome-icon" />
            <Typography.Title className="welcome-title" level={3}>
              你好，我是阿福
            </Typography.Title>
            <Typography.Text type="secondary">有什么我可以帮助你的吗？</Typography.Text>
          </Flex>
        </div>
        <div className="chat-sender-area">
          <Sender
            autoSize={{ maxRows: 3, minRows: 1 }}
            classNames={{ root: "chat-sender-box" }}
            footer={renderSenderFooter}
            loading={isLoading}
            onCancel={handleStop}
            onChange={setInput}
            onSubmit={handleSenderSubmit}
            placeholder="输入消息..."
            suffix={false}
            value={input}
          />
        </div>
      </div>
    );
  }

  return (
    <div className="app-chat-panel">
      {loadingHistory ? (
        <div className="app-chat-panel-loading">
          <Spin />
        </div>
      ) : (
        <ChatScrollArea messages={messages} status={status}>
          <Flex gap={8} vertical>
            {messages.map((msg, idx) => (
              <Card
                classNames={{ extra: "card-extra-actions" }}
                extra={getCardExtra(msg, idx)}
                key={msg.id}
                size="small"
                title={msg.role === "user" ? "用户" : <span className="msg-title-ai">阿福</span>}
              >
                <div className="message-body">
                  {editingMessageId === msg.id ? (
                    <Flex gap={8} vertical>
                      <Input.TextArea
                        autoSize={{ maxRows: 6, minRows: 1 }}
                        onChange={(e) => setEditText(e.target.value)}
                        value={editText}
                      />
                      <Flex gap={8} justify="flex-end">
                        <Button onClick={handleEditCancel} size="small">
                          取消
                        </Button>
                        <Button onClick={handleEditConfirm} size="small" type="primary">
                          确认
                        </Button>
                      </Flex>
                    </Flex>
                  ) : (
                    msg.parts.map((part: Record<string, unknown>, i: number) => (
                      <PartRenderer
                        isStreaming={isLoading && idx === messages.length - 1 && msg.role === "assistant"}
                        key={i}
                        part={part}
                        role={msg.role}
                      />
                    ))
                  )}
                </div>
              </Card>
            ))}
            {isLoading && (
              <Flex className="chat-loading-indicator" justify="center">
                <Spin size="small" />
              </Flex>
            )}
          </Flex>
        </ChatScrollArea>
      )}
      <div className="chat-sender-area">
        <Sender
          autoSize={{ maxRows: 3, minRows: 1 }}
          classNames={{ root: "chat-sender-box" }}
          footer={renderSenderFooter}
          loading={isLoading}
          onCancel={handleStop}
          onChange={setInput}
          onSubmit={handleSenderSubmit}
          placeholder="输入消息..."
          suffix={false}
          value={input}
        />
      </div>
    </div>
  );
}

function PartRenderer({
  isStreaming,
  part,
  role,
}: {
  isStreaming: boolean;
  part: Record<string, unknown>;
  role: string;
}) {
  const partType = typeof part["type"] === "string" ? part["type"] : "";

  if (partType === "text") {
    return <TextPart isStreaming={isStreaming} part={part} role={role} />;
  }
  if (partType.startsWith("tool-")) {
    return <ToolPart part={part} />;
  }
  if (partType === "reasoning") {
    return <ReasoningPart part={part} />;
  }
  return null;
}
