import type { CSSProperties } from "react";
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
import {
  ArrowUp,
  ChevronDown,
  Copy,
  Ellipsis,
  LogOut,
  MessageCirclePlus,
  Paperclip,
  PanelLeftClose,
  PanelLeftOpen,
  Pencil,
  Pin,
  RotateCcw,
  Search,
  Settings,
  Share2,
  Smartphone,
  Trash2,
  User,
  Zap,
  CircleHelp,
} from "lucide-react";
import {
  Avatar,
  Button,
  Collapse,
  Drawer,
  Dropdown,
  Input,
  Layout,
  Modal,
  Select,
  Tooltip,
  Typography,
} from "antd";
import type { MenuProps } from "antd";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "sonner";
import logoSrc from "../assets/logo.png";
import logoTitleSrc from "../assets/logo-title.png";
import { ACCESS_TOKEN_KEY, SessionExpiredError, USER_KEY, logout } from "../api/auth";
import {
  createChatSession,
  deleteChatSession,
  listChatSessionMessages,
  listChatSessions,
  normalizeSessionMessages,
  sessionDisplayTitle,
  sessionStableId,
  updateChatSessionTitle,
  type ChatSession,
  type ChatTurnForUi,
} from "../api/chatSessions";
import { isAbortLikeError, streamQwenChat, type ChatMessagePayload } from "../api/qwenChat";
import StreamMessage from "../components/StreamMessage";

const { Header, Sider, Content } = Layout;
const MOBILE_WIDTH = 750;

type UiMessage = ChatTurnForUi;

type StoredUserProfile = {
  nickname: string;
  avatarUrl: string;
};

function parseStoredUserProfileJson(raw: string | null): { displayName: string; avatar?: string } {
  try {
    if (!raw) return { displayName: "用户" };
    const user = JSON.parse(raw) as StoredUserProfile;
    const displayName = user.nickname.trim() || "用户";
    const avatar = user.avatarUrl || undefined;
    return { displayName, avatar };
  } catch {
    return { displayName: "用户" };
  }
}

/** 与 `persistUser` 内触发的 `chatone-user-changed` 及跨标签 `storage` 对齐，用于刷新侧栏昵称 */
function subscribeUserProfile(onChange: () => void) {
  const onStorage = (e: StorageEvent) => {
    if (e.key === USER_KEY || e.key === null) onChange();
  };
  window.addEventListener("chatone-user-changed", onChange);
  window.addEventListener("storage", onStorage);
  return () => {
    window.removeEventListener("chatone-user-changed", onChange);
    window.removeEventListener("storage", onStorage);
  };
}

const INITIAL_MESSAGES: UiMessage[] = [];

const STREAM_TIMEOUT_MS = 90000;
const MAX_STREAM_RETRY = 1;
const CONTEXT_WINDOW_SIZE = 8;
const DEEP_THINK_STORAGE_KEY = "chatone-deep-think-enabled";
const SMART_SEARCH_STORAGE_KEY = "chatone-smart-search-enabled";
const SELECTED_MODEL_STORAGE_KEY = "chatone-selected-model";
const LOGO_SRC = logoSrc;
const TITLE_SRC = logoTitleSrc;

/** 展示名与请求 model 字段；需与后端实际支持的模型 id 一致 */
const DEFAULT_QWEN_MODEL = "qwen3.6-plus";
const QWEN_MODEL_OPTIONS: { value: string; label: string }[] = [
  { value: "qwen3-max", label: "Qwen3-Max" },
  { value: DEFAULT_QWEN_MODEL, label: "Qwen3.6-Plus" },
  { value: "qwen3.5-flash", label: "Qwen3.5-Flash" },
  { value: "qwen-plus", label: "Qwen-Plus" },
  { value: "deepseek-r1", label: "DeepSeek-R1" },
  { value: "kimi-k2.6", label: "Kimi-K2.6" },
];

function replaceSessionUrl(sessionId: string) {
  const base = import.meta.env.BASE_URL ?? "/";
  const prefix = base === "/" ? "" : base.replace(/\/$/, "");
  const nextPath = `${prefix}/s/${encodeURIComponent(sessionId)}`;
  window.history.replaceState(window.history.state, "", nextPath);
}

export default function HomePage() {
  const [modal, modalContextHolder] = Modal.useModal();
  const navigate = useNavigate();
  const { sessionId: routeSessionId } = useParams<{ sessionId?: string }>();
  const [collapsed, setCollapsed] = useState(false);
  const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
  const [isMobile, setIsMobile] = useState(false);
  const [inputValue, setInputValue] = useState("");
  const [messages, setMessages] = useState<UiMessage[]>(INITIAL_MESSAGES);
  const [isSending, setIsSending] = useState(false);
  const [deepThink, setDeepThink] = useState(() => {
    try {
      return window.localStorage.getItem(DEEP_THINK_STORAGE_KEY) === "1";
    } catch {
      return false;
    }
  });
  const [smartSearch, setSmartSearch] = useState(() => {
    try {
      return window.localStorage.getItem(SMART_SEARCH_STORAGE_KEY) === "1";
    } catch {
      return false;
    }
  });
  const [selectedModel, setSelectedModel] = useState(() => {
    try {
      const raw = window.localStorage.getItem(SELECTED_MODEL_STORAGE_KEY);
      const supported = QWEN_MODEL_OPTIONS.some((item) => item.value === raw);
      return supported && raw ? raw : DEFAULT_QWEN_MODEL;
    } catch {
      return DEFAULT_QWEN_MODEL;
    }
  });
  const messageListRef = useRef<HTMLDivElement | null>(null);
  const shouldStickToBottomRef = useRef(true);
  const abortRef = useRef<AbortController | null>(null);
  const [showScrollToBottom, setShowScrollToBottom] = useState(false);

  const [sessions, setSessions] = useState<ChatSession[]>([]);
  const [sessionsLoading, setSessionsLoading] = useState(false);
  const [sessionMutationBusy, setSessionMutationBusy] = useState(false);
  const [sessionActionBusyId, setSessionActionBusyId] = useState<string | null>(null);
  const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
  const activeSessionIdRef = useRef<string | null>(null);

  const userJsonSnapshot = useSyncExternalStore(
    subscribeUserProfile,
    () => window.localStorage.getItem(USER_KEY) ?? "",
    () => "",
  );
  const userProfile = useMemo(
    () => parseStoredUserProfileJson(userJsonSnapshot || null),
    [userJsonSnapshot],
  );

  useEffect(() => {
    activeSessionIdRef.current = activeSessionId;
  }, [activeSessionId]);

  useEffect(() => {
    window.localStorage.setItem(DEEP_THINK_STORAGE_KEY, deepThink ? "1" : "0");
  }, [deepThink]);

  useEffect(() => {
    window.localStorage.setItem(SMART_SEARCH_STORAGE_KEY, smartSearch ? "1" : "0");
  }, [smartSearch]);

  useEffect(() => {
    window.localStorage.setItem(SELECTED_MODEL_STORAGE_KEY, selectedModel);
  }, [selectedModel]);

  /** 用户信息下拉：略增大行高与项间距 */
  const userMenuItemStyle: CSSProperties = {
    height: "auto",
    lineHeight: 1.45,
    paddingBlock: 10,
    marginBlock: 3,
  };
  const userMenuDividerStyle: CSSProperties = { margin: "10px 0" };

  const userMenuItems: MenuProps["items"] = [
    { key: "app", icon: <Smartphone size={16} />, label: "下载手机应用", style: userMenuItemStyle },
    { key: "settings", icon: <Settings size={16} />, label: "系统设置", style: userMenuItemStyle },
    {
      key: "help",
      icon: <CircleHelp size={16} />,
      label: "帮助与反馈",
      style: userMenuItemStyle,
    },
    { type: "divider", style: userMenuDividerStyle },
    {
      key: "logout",
      icon: <LogOut size={16} />,
      label: "退出登录",
      danger: true,
      style: userMenuItemStyle,
    },
  ];

  const onUserMenuClick: MenuProps["onClick"] = async ({ key }) => {
    if (key === "logout") {
      abortRef.current?.abort();
      setIsSending(false);
      await logout();
      toast.success("已退出登录");
      navigate("/login", { replace: true });
      return;
    }
    if (key === "app" || key === "settings" || key === "help") {
      toast.info("功能开发中");
    }
  };

  useEffect(() => {
    const accessToken = window.localStorage.getItem(ACCESS_TOKEN_KEY);
    if (!accessToken) {
      navigate("/login", { replace: true });
    }
  }, [navigate]);

  useEffect(() => {
    const updateIsMobile = () => {
      const w = Math.min(
        window.innerWidth,
        document.documentElement.clientWidth || window.innerWidth,
      );
      const mobile = w < MOBILE_WIDTH;
      setIsMobile(mobile);
      if (!mobile) setMobileSidebarOpen(false);
    };
    updateIsMobile();
    window.addEventListener("resize", updateIsMobile);
    window.addEventListener("orientationchange", updateIsMobile);
    return () => {
      window.removeEventListener("resize", updateIsMobile);
      window.removeEventListener("orientationchange", updateIsMobile);
    };
  }, []);

  const isMessageListAtBottom = useCallback(() => {
    const el = messageListRef.current;
    if (!el) return true;
    // 允许少量像素误差，避免小数像素造成闪烁。
    return el.scrollHeight - el.scrollTop - el.clientHeight <= 24;
  }, []);

  const scrollMessageListToBottom = useCallback((behavior: ScrollBehavior = "smooth") => {
    const el = messageListRef.current;
    if (!el) return;
    el.scrollTo({ top: el.scrollHeight, behavior });
  }, []);

  useEffect(() => {
    const el = messageListRef.current;
    if (!el) return;
    const onScroll = () => {
      const atBottom = isMessageListAtBottom();
      shouldStickToBottomRef.current = atBottom;
      setShowScrollToBottom(!atBottom);
    };
    onScroll();
    el.addEventListener("scroll", onScroll, { passive: true });
    return () => {
      el.removeEventListener("scroll", onScroll);
    };
  }, [isMessageListAtBottom]);

  useEffect(() => {
    if (shouldStickToBottomRef.current) {
      scrollMessageListToBottom("auto");
      setShowScrollToBottom(false);
    } else {
      setShowScrollToBottom(true);
    }
  }, [messages, scrollMessageListToBottom]);

  const refreshSessionList = useCallback(async (): Promise<ChatSession[]> => {
    if (!window.localStorage.getItem(ACCESS_TOKEN_KEY)) return [];
    setSessionsLoading(true);
    try {
      const list = await listChatSessions({ limit: 50 });
      setSessions(list);
      return list;
    } catch {
      toast.error("会话列表加载失败");
      return [] as ChatSession[];
    } finally {
      setSessionsLoading(false);
    }
  }, []);

  useEffect(() => {
    if (!window.localStorage.getItem(ACCESS_TOKEN_KEY)) return;
    const id = window.setTimeout(() => {
      void refreshSessionList();
    }, 0);
    return () => window.clearTimeout(id);
  }, [refreshSessionList]);

  /**
   * 打开指定会话并加载历史消息。
   * 默认会把当前会话 id 同步到路由；当由路由参数驱动时可关闭 URL 更新，避免重复跳转。
   */
  const openSession = useCallback(
    async (sessionId: string, options?: { updateUrl?: boolean }) => {
      if (options?.updateUrl ?? true) {
        navigate(`/s/${encodeURIComponent(sessionId)}`);
      }
      setActiveSessionId(sessionId);
      try {
        const rows = await listChatSessionMessages(sessionId, { limit: 100 });
        const normalized = normalizeSessionMessages(rows);
        setMessages(normalized.length > 0 ? normalized : INITIAL_MESSAGES.map((m) => ({ ...m })));
      } catch {
        toast.error("加载会话消息失败");
      }
    },
    [navigate],
  );

  useEffect(() => {
    if (!window.localStorage.getItem(ACCESS_TOKEN_KEY)) return;
    if (!routeSessionId) {
      const id = window.setTimeout(() => {
        setActiveSessionId(null);
        setMessages(INITIAL_MESSAGES.map((m) => ({ ...m })));
      }, 0);
      return () => window.clearTimeout(id);
    }
    // 路由已与当前会话一致时，不重复加载，避免覆盖流式中的本地消息状态。
    if (routeSessionId === activeSessionIdRef.current) {
      return;
    }
    const id = window.setTimeout(() => {
      void openSession(routeSessionId, { updateUrl: false });
    }, 0);
    return () => window.clearTimeout(id);
  }, [openSession, routeSessionId]);

  const handleNewSession = async () => {
    setActiveSessionId(null);
    setMessages(INITIAL_MESSAGES.map((m) => ({ ...m })));
    navigate("/");
  };

  const handleRenameSession = useCallback(
    (session: ChatSession) => {
      const sid = sessionStableId(session);
      if (!sid || sessionActionBusyId) return;
      let nextTitle = session.title;
      modal.confirm({
        centered: true,
        title: "重命名会话",
        content: (
          <Input
            autoFocus
            maxLength={200}
            defaultValue={session.title}
            placeholder="请输入会话标题"
            onChange={(event) => {
              nextTitle = event.target.value;
            }}
            onPressEnter={(event) => {
              event.preventDefault();
            }}
          />
        ),
        okText: "保存",
        cancelText: "取消",
        async onOk() {
          if (nextTitle === session.title) return;
          setSessionActionBusyId(sid);
          try {
            await updateChatSessionTitle(sid, { title: nextTitle });
            await refreshSessionList();
            toast.success("会话已重命名");
          } catch (error) {
            const text = error instanceof Error ? error.message : "重命名会话失败";
            toast.error(text);
            throw error;
          } finally {
            setSessionActionBusyId(null);
          }
        },
      });
    },
    [modal, refreshSessionList, sessionActionBusyId],
  );

  const handleDeleteSession = useCallback(
    (session: ChatSession) => {
      const sid = sessionStableId(session);
      if (!sid || sessionActionBusyId) return;
      modal.confirm({
        centered: true,
        title: "删除会话",
        content: "确认删除该会话？删除后不可恢复。",
        okText: "删除",
        okType: "danger",
        cancelText: "取消",
        async onOk() {
          setSessionActionBusyId(sid);
          try {
            await deleteChatSession(sid);
            if (activeSessionId === sid) {
              abortRef.current?.abort();
              setIsSending(false);
              setActiveSessionId(null);
              setMessages(INITIAL_MESSAGES.map((m) => ({ ...m })));
              navigate("/");
            }
            await refreshSessionList();
            toast.success("会话已删除");
          } catch (error) {
            const text = error instanceof Error ? error.message : "删除会话失败";
            toast.error(text);
            throw error;
          } finally {
            setSessionActionBusyId(null);
          }
        },
      });
    },
    [activeSessionId, modal, navigate, refreshSessionList, sessionActionBusyId],
  );

  const handleCopyUserMessage = useCallback(async (content: string) => {
    try {
      await navigator.clipboard.writeText(content);
      toast.success("已复制");
    } catch {
      toast.error("复制失败，请检查浏览器权限");
    }
  }, []);

  const handleCopyAssistantMessage = useCallback(async (content: string) => {
    try {
      await navigator.clipboard.writeText(content);
      toast.success("已复制");
    } catch {
      toast.error("复制失败，请检查浏览器权限");
    }
  }, []);

  const sendMessage = async () => {
    const trimmed = inputValue.trim();
    if (!trimmed || isSending) return;
    let targetSessionId = activeSessionId;
    if (!targetSessionId) {
      if (sessionMutationBusy) return;
      setSessionMutationBusy(true);
      try {
        const row = await createChatSession({
          // 首条提问作为会话初始标题（后端限制 200）。
          title: trimmed.slice(0, 200),
        });
        targetSessionId = row.id;
        setActiveSessionId(row.id);
        replaceSessionUrl(row.id);
        // 侧栏列表后台刷新，不阻塞当前消息发送。
        void refreshSessionList();
      } catch (e) {
        const text = e instanceof Error ? e.message : "新建会话失败";
        toast.error(text);
        return;
      } finally {
        setSessionMutationBusy(false);
      }
    }

    setInputValue("");
    setIsSending(true);

    const userMessage: UiMessage = {
      id: `user-${Date.now()}`,
      role: "user",
      content: trimmed,
    };
    const assistantMessageId = `assistant-${Date.now() + 1}`;
    const assistantPlaceholder: UiMessage = {
      id: assistantMessageId,
      role: "assistant",
      content: "",
      thinking: deepThink ? "正在思考并生成答案…" : undefined,
    };

    const nextMessages = [...messages, userMessage, assistantPlaceholder];
    setMessages(nextMessages);

    const payloadMessages: ChatMessagePayload[] = nextMessages.map((item) => ({
      role: item.role,
      content: item.content,
    }));

    let assistantText = "";
    let lastError: unknown = null;

    for (let attempt = 0; attempt <= MAX_STREAM_RETRY; attempt += 1) {
      const controller = new AbortController();
      abortRef.current = controller;
      let receivedToken = false;

      const retryMessages =
        attempt === 0
          ? payloadMessages
          : ([
              ...nextMessages
                .slice(-CONTEXT_WINDOW_SIZE)
                .map((item): ChatMessagePayload => ({ role: item.role, content: item.content })),
              {
                role: "user" as const,
                content: `网络抖动导致输出中断。请在不重复已输出内容的前提下继续回答。已输出尾部：${assistantText.slice(-80)}`,
              },
            ] satisfies ChatMessagePayload[]);

      try {
        await streamQwenChat({
          messages: retryMessages,
          model: selectedModel,
          sessionId: targetSessionId ?? undefined,
          enableWebSearch: smartSearch,
          enableThinking: deepThink,
          signal: controller.signal,
          timeoutMs: STREAM_TIMEOUT_MS,
          onToken: (token) => {
            receivedToken = true;
            assistantText += token;
            setMessages((prev) =>
              prev.map((item) =>
                item.id === assistantMessageId
                  ? { ...item, content: `${item.content}${token}` }
                  : item,
              ),
            );
          },
        });
        lastError = null;
        break;
      } catch (error) {
        if (error instanceof SessionExpiredError) {
          lastError = null;
          break;
        }
        lastError = error;
        const isAbortLike = isAbortLikeError(error);
        const canRetry = attempt < MAX_STREAM_RETRY;
        if (!canRetry) break;
        // 网络抖动或超时时自动重连；若已收到部分 token 也保留已输出文本。
        if (isAbortLike || !receivedToken) {
          await new Promise((resolve) => window.setTimeout(resolve, 300));
          continue;
        }
        break;
      } finally {
        abortRef.current = null;
      }
    }

    if (lastError) {
      const message = lastError instanceof Error ? lastError.message : "请求失败，请稍后重试";
      setMessages((prev) =>
        prev.map((item) =>
          item.id === assistantMessageId
            ? {
                ...item,
                content: item.content || `接口调用失败：${message}`,
                thinking: undefined,
              }
            : item,
        ),
      );
    }

    setIsSending(false);
    setMessages((prev) =>
      prev.map((item) =>
        item.id === assistantMessageId ? { ...item, thinking: undefined } : item,
      ),
    );
  };

  const sidebarContent = (
    <div className="flex min-h-0 w-full min-w-0 flex-1 flex-col bg-[var(--ds-bg-sider)]">
      <div className="shrink-0 px-3 pt-3 pb-2">
        <Button
          block
          loading={sessionMutationBusy}
          disabled={sessionsLoading}
          className="h-10! rounded-xl! border-[var(--ds-border)]! bg-[var(--ds-bg-main)]! text-[13px]! font-medium shadow-none hover:border-sky-300! hover:text-sky-600!"
          icon={<MessageCirclePlus size={14} strokeWidth={2} />}
          onClick={() => {
            void handleNewSession();
          }}
        >
          {(!collapsed || isMobile) && "新建会话"}
        </Button>
      </div>

      <div className="min-h-0 flex-1 overflow-auto px-2 py-2">
        {sessionsLoading ? (
          <Typography.Text className="block px-2 text-[12px]" type="secondary">
            加载会话…
          </Typography.Text>
        ) : sessions.length === 0 ? (
          <Typography.Text className="block px-2 text-[12px]" type="secondary">
            暂无会话，点击「新建会话」开始
          </Typography.Text>
        ) : (
          <div className="mb-4">
            <Typography.Text
              className="mb-2 block px-2 text-[11px] uppercase tracking-wide"
              style={{ color: "var(--ds-text-secondary)" }}
            >
              会话
            </Typography.Text>
            {sessions.map((s) => {
              const sid = sessionStableId(s);
              if (!sid) return null;
              const title = sessionDisplayTitle(s);
              const active = sid === activeSessionId;
              const itemMenu: MenuProps = {
                items: [
                  { key: "rename", icon: <Pencil size={16} />, label: "重命名" },
                  { key: "pin", icon: <Pin size={16} />, label: "置顶" },
                  { key: "delete", icon: <Trash2 size={16} />, danger: true, label: "删除" },
                ],
                onClick: ({ key, domEvent }) => {
                  domEvent.stopPropagation();
                  if (key === "rename") void handleRenameSession(s);
                  if (key === "pin") toast.info("置顶功能开发中");
                  if (key === "delete") void handleDeleteSession(s);
                },
              };
              return (
                <div
                  key={sid}
                  className="group mb-1.5 flex w-full items-center gap-1 rounded-2xl pr-1 text-[14px] text-neutral-700 transition-colors hover:bg-black/5"
                  style={{
                    background: active ? "var(--ds-active-item)" : undefined,
                    color: active ? "var(--ds-accent)" : undefined,
                  }}
                >
                  <button
                    type="button"
                    onClick={() => {
                      void openSession(sid);
                    }}
                    className="flex h-11 min-w-0 flex-1 cursor-pointer items-center rounded-2xl px-4 text-left"
                  >
                    {(!collapsed || isMobile) && (
                      <span className="min-w-0 flex-1 truncate text-[14px] leading-none">
                        {title}
                      </span>
                    )}
                  </button>
                  {(!collapsed || isMobile) && (
                    <div className={`${active ? "flex" : "hidden group-hover:flex"}`}>
                      <Dropdown menu={itemMenu} trigger={["click"]} placement="bottomRight">
                        <Button
                          type="text"
                          size="small"
                          icon={<Ellipsis size={16} />}
                          loading={sessionActionBusyId === sid}
                          className="!flex !h-8 !w-8 !items-center !justify-center rounded-xl text-neutral-500 hover:bg-black/5!"
                          onClick={(event) => {
                            event.stopPropagation();
                          }}
                        />
                      </Dropdown>
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        )}
      </div>

      <div className="shrink-0 p-3">
        <Dropdown
          menu={{ items: userMenuItems, onClick: onUserMenuClick }}
          placement="topLeft"
          trigger={["click"]}
        >
          <button
            type="button"
            className={`flex w-full cursor-pointer items-center gap-2 rounded-xl bg-neutral-100 px-2 py-2 text-left transition-colors hover:bg-neutral-200/90 ${
              collapsed && !isMobile ? "justify-center py-2.5" : ""
            }`}
          >
            <Avatar
              size={36}
              src={userProfile.avatar}
              icon={!userProfile.avatar ? <User size={18} /> : undefined}
              className="shrink-0 bg-neutral-300"
            />
            {(!collapsed || isMobile) && (
              <>
                <span className="min-w-0 flex-1 truncate text-[13px] font-medium text-neutral-800">
                  {userProfile.displayName}
                </span>
                <Ellipsis size={16} className="shrink-0 text-neutral-500" />
              </>
            )}
          </button>
        </Dropdown>
      </div>
    </div>
  );

  return (
    <Layout
      className="chat-one-shell flex h-dvh max-h-dvh overflow-hidden"
      style={{ background: "var(--ds-bg-main)" }}
    >
      {modalContextHolder}
      {!isMobile && (
        <Sider
          trigger={null}
          collapsible
          collapsed={collapsed}
          width={260}
          collapsedWidth={72}
          theme="light"
          className="!min-h-0 !h-full !overflow-hidden !bg-[var(--ds-bg-sider)]"
        >
          <div className="flex h-full min-h-0 min-w-0 flex-col">
            <div className="flex h-14 shrink-0 items-center px-3">
              <img src={LOGO_SRC} alt="" className="w-7 shrink-0 object-contain" decoding="async" />
              {!collapsed && (
                <img
                  src={TITLE_SRC}
                  alt="chatone"
                  className="w-auto max-w-[106px] object-contain object-left"
                  decoding="async"
                />
              )}
            </div>
            {sidebarContent}
          </div>
        </Sider>
      )}

      <Layout
        className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden"
        style={{ background: "var(--ds-bg-main)" }}
      >
        <Header
          className="!flex !h-14 !shrink-0 !items-center !justify-between !bg-[var(--ds-bg-main)] !px-5"
          style={{ lineHeight: "56px" }}
        >
          <div className="flex items-center gap-3">
            <Button
              type="text"
              className="text-neutral-500 hover:bg-black/5!"
              icon={
                isMobile ? (
                  <PanelLeftOpen size={16} />
                ) : collapsed ? (
                  <PanelLeftOpen size={16} />
                ) : (
                  <PanelLeftClose size={16} />
                )
              }
              onClick={() => {
                if (isMobile) setMobileSidebarOpen(true);
                else setCollapsed((v) => !v);
              }}
            />
            <img
              src={LOGO_SRC}
              alt=""
              width={24}
              height={24}
              className="h-6 w-6 shrink-0 object-contain md:hidden"
              decoding="async"
            />
            <Select
              className="min-w-[190px]"
              variant="borderless"
              popupMatchSelectWidth={false}
              options={QWEN_MODEL_OPTIONS}
              value={selectedModel}
              onChange={setSelectedModel}
              disabled={isSending}
              aria-label="选择对话模型"
            />
          </div>
          <span className="rounded-full border border-[var(--ds-border)] bg-neutral-50 px-3 py-1 text-xs text-neutral-500">
            快速模式
          </span>
        </Header>

        <Content className="flex min-h-0 flex-1 flex-col overflow-hidden !bg-[var(--ds-bg-main)]">
          <div className="relative flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
            <div
              ref={messageListRef}
              className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-4 pt-6 pb-0 md:px-8 md:pt-6 md:pb-0"
            >
              <div className="mx-auto flex max-w-4xl flex-col gap-5">
                {!activeSessionId && messages.length === 0 ? (
                  <p className="px-4 text-[18px] text-neutral-800">你好，我是 ChatOne</p>
                ) : (
                  messages.map((item) => (
                    <div
                      key={item.id}
                      className={`flex ${item.role === "user" ? "justify-end" : "justify-start"}`}
                    >
                      <div
                        className={
                          item.role === "user" ? "group max-w-full px-4 " : "group w-full space-y-2"
                        }
                      >
                        {item.role === "user" && (
                          <div
                            className="rounded-2xl px-4 py-2.5 text-[15px] leading-relaxed text-neutral-800"
                            style={{ background: "var(--ds-user-bubble)" }}
                          >
                            {item.content}
                          </div>
                        )}
                        {item.role === "assistant" && item.thinking && (
                          <Collapse
                            bordered={false}
                            size="small"
                            className="!mb-2 !rounded-xl !bg-transparent"
                            items={[
                              {
                                key: "think",
                                label: (
                                  <span className="text-[13px] text-neutral-600">
                                    <Zap size={14} className="mr-1.5" />
                                    已思考（用时约 2 秒）
                                  </span>
                                ),
                                children: (
                                  <Typography.Text type="secondary" className="text-[13px]">
                                    {item.thinking}
                                  </Typography.Text>
                                ),
                              },
                            ]}
                          />
                        )}
                        {item.role === "assistant" &&
                          (item.content ? (
                            <StreamMessage content={item.content} />
                          ) : (
                            <div className="rounded-2xl px-4 py-3 text-[14px] text-neutral-500">
                              <span className="inline-flex items-center gap-1">
                                <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-neutral-400 [animation-delay:-0.3s]" />
                                <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-neutral-400 [animation-delay:-0.15s]" />
                                <span className="h-1.5 w-1.5 animate-bounce rounded-full bg-neutral-400" />
                              </span>
                            </div>
                          ))}
                        {item.role === "assistant" && item.content && (
                          <>
                            <p className="mt-3 px-4 text-[12px] text-neutral-500 italic">
                              本回答由 AI 生成，内容仅供参考，请仔细甄别。
                            </p>
                            <div className="mt-2 flex h-8 items-center gap-1 px-4 text-neutral-500 opacity-0 pointer-events-none transition-opacity group-hover:opacity-100 group-hover:pointer-events-auto">
                              <Tooltip title="复制">
                                <Button
                                  type="text"
                                  size="small"
                                  icon={<Copy size={16} />}
                                  className="!px-1 text-neutral-500 hover:bg-black/5!"
                                  aria-label="复制回复"
                                  onClick={() => {
                                    void handleCopyAssistantMessage(item.content);
                                  }}
                                />
                              </Tooltip>
                              <Tooltip title="重新生成">
                                <Button
                                  type="text"
                                  size="small"
                                  icon={<RotateCcw size={16} />}
                                  className="!px-1 text-neutral-500 hover:bg-black/5!"
                                  aria-label="重新生成"
                                  onClick={() => {
                                    toast.info("重新生成功能开发中");
                                  }}
                                />
                              </Tooltip>
                              <Tooltip title="分享">
                                <Button
                                  type="text"
                                  size="small"
                                  icon={<Share2 size={16} />}
                                  className="!px-1 text-neutral-500 hover:bg-black/5!"
                                  aria-label="分享"
                                  onClick={() => {
                                    toast.info("分享功能开发中");
                                  }}
                                />
                              </Tooltip>
                            </div>
                          </>
                        )}
                        {item.role === "user" && (
                          <div className="mt-2 flex h-7 items-center justify-end gap-1 text-neutral-500 opacity-0 pointer-events-none transition-opacity group-hover:opacity-100 group-hover:pointer-events-auto">
                            <Button
                              type="text"
                              size="small"
                              icon={<Copy size={16} />}
                              className="text-neutral-500 hover:bg-black/5!"
                              aria-label="复制消息"
                              onClick={() => {
                                void handleCopyUserMessage(item.content);
                              }}
                            />
                            <Button
                              type="text"
                              size="small"
                              icon={<Pencil size={16} />}
                              className="text-neutral-500 hover:bg-black/5!"
                              aria-label="编辑消息"
                              onClick={() => {
                                toast.info("编辑功能开发中");
                              }}
                            />
                          </div>
                        )}
                      </div>
                    </div>
                  ))
                )}
              </div>
            </div>

            <div className="shrink-0 bg-[var(--ds-bg-main)] px-4 pt-0 md:px-8 md:pt-0">
              <div className="mx-auto max-w-4xl">
                <div className="relative">
                  {showScrollToBottom && (
                    <button
                      type="button"
                      aria-label="滚动到底部"
                      className="absolute -top-14 right-3 z-10 flex size-9 shrink-0 cursor-pointer items-center justify-center rounded-full border border-neutral-200/90 bg-white text-neutral-900 shadow-md transition-colors hover:bg-neutral-50"
                      onClick={() => {
                        shouldStickToBottomRef.current = true;
                        setShowScrollToBottom(false);
                        scrollMessageListToBottom("smooth");
                      }}
                    >
                      <ChevronDown color="#666" size={18} strokeWidth={2} aria-hidden />
                    </button>
                  )}
                  <div className="rounded-3xl border border-[var(--ds-border)] bg-neutral-50/50 p-3 shadow-sm">
                    <Input.TextArea
                      value={inputValue}
                      onChange={(e) => setInputValue(e.target.value)}
                      onPressEnter={(e) => {
                        if (e.shiftKey) return;
                        e.preventDefault();
                        void sendMessage();
                      }}
                      placeholder="给 ChatOne 发送消息"
                      variant="borderless"
                      autoSize={{ minRows: 2, maxRows: 8 }}
                      className="!px-1 !text-[15px] placeholder:text-neutral-400"
                    />
                    <div className="mt-2 flex flex-wrap items-center justify-between gap-2">
                      <div className="flex flex-wrap gap-2">
                        <button
                          type="button"
                          onClick={() => setDeepThink((v) => !v)}
                          className={`inline-flex cursor-pointer items-center gap-1.5 rounded-full border px-3 py-1.5 text-xs transition-colors ${
                            deepThink
                              ? "border-sky-300 bg-sky-50 text-sky-700"
                              : "border-[var(--ds-border)] bg-[var(--ds-bg-main)] text-neutral-600 hover:border-neutral-300"
                          }`}
                        >
                          <Zap size={14} />
                          深度思考
                        </button>
                        <button
                          type="button"
                          onClick={() => setSmartSearch((v) => !v)}
                          className={`inline-flex cursor-pointer items-center gap-1.5 rounded-full border px-3 py-1.5 text-xs transition-colors ${
                            smartSearch
                              ? "border-sky-300 bg-sky-50 text-sky-700"
                              : "border-[var(--ds-border)] bg-[var(--ds-bg-main)] text-neutral-600 hover:border-neutral-300"
                          }`}
                        >
                          <Search size={14} />
                          智能搜索
                        </button>
                      </div>
                      <div className="flex items-center gap-2">
                        <Button
                          type="text"
                          icon={<Paperclip size={16} className="text-neutral-400" />}
                          className="text-neutral-400"
                        />
                        <Button
                          type="primary"
                          shape="circle"
                          icon={<ArrowUp size={16} />}
                          loading={isSending}
                          disabled={isSending || !inputValue.trim()}
                          onClick={() => {
                            void sendMessage();
                          }}
                          className="!flex !h-9 !w-9 !items-center !justify-center !border-0 !shadow-none"
                          style={{
                            background: "var(--ds-send)",
                          }}
                        />
                      </div>
                    </div>
                  </div>
                </div>
                <p className="m-0 py-2 text-center text-[11px] text-neutral-500">
                  内容由 AI 生成，请注意核实
                </p>
              </div>
            </div>
          </div>
        </Content>
      </Layout>

      <Drawer
        title="ChatOne"
        placement="left"
        closable
        onClose={() => setMobileSidebarOpen(false)}
        open={isMobile && mobileSidebarOpen}
        width={280}
        styles={{
          body: {
            padding: 0,
            background: "var(--ds-bg-sider)",
            display: "flex",
            flexDirection: "column",
            height: "100%",
            minHeight: 0,
          },
        }}
      >
        {sidebarContent}
      </Drawer>
    </Layout>
  );
}
