"use client";

import { Ticker } from "@tombcato/smart-ticker";
import { FilesIcon, ListTodoIcon, XIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";

import { ConversationEmptyState } from "@/components/ai-elements/conversation";
import { Button } from "@/components/ui/button";
import {
  DevDialog,
  DevDialogContent,
  DevDialogFooter,
  DevDialogHeader,
  DevDialogTitle,
} from "@/components/ui/dev-dialog";
import { useSidebar } from "@/components/ui/sidebar";
import {
  ArtifactFileDetail,
  ArtifactFileList,
  useArtifacts,
} from "@/components/workspace/artifacts";
import { useThreadChat } from "@/components/workspace/chats";
// import { DevTodoList } from "@/components/workspace/dev-todo-list";
import { IframeTestPanel } from "@/components/workspace/iframe-test-panel";
import { InputBox } from "@/components/workspace/input-box";
import { MessageList } from "@/components/workspace/messages";
import { ThreadContext } from "@/components/workspace/messages/context";
import { ThreadTitle } from "@/components/workspace/thread-title";
import { Tooltip } from "@/components/workspace/tooltip";
import { useSpecificChatMode } from "@/components/workspace/use-chat-mode";
import { Welcome } from "@/components/workspace/welcome";
import { getAPIClient } from "@/core/api";
import { sanitizeArtifactPaths } from "@/core/artifacts/utils";
import { useI18n } from "@/core/i18n/hooks";
import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages";
import { useNotification } from "@/core/notification/hooks";
import { useLocalSettings } from "@/core/settings";
import { useThreadStream } from "@/core/threads/hooks";
import { textOfMessage } from "@/core/threads/utils";
import { env } from "@/env";
import { useSelectedSkillListener } from "@/hooks/use-selected-skill-listener";
import { cn } from "@/lib/utils";

import "@tombcato/smart-ticker/style.css";
import motivationSlogans from "./motivation-slogans.json";

export default function ChatPage() {
  const { t } = useI18n();
  useSpecificChatMode();
  const [sloganIndex, setSloganIndex] = useState(0);
  const [settings, setSettings] = useLocalSettings();
  const { setOpen: setSidebarOpen } = useSidebar();
  const router = useRouter();
  const {
    artifacts,
    open: artifactsOpen,
    setOpen: setArtifactsOpen,
    setArtifacts,
    select: selectArtifact,
    selectedArtifact,
    deselect: deselectArtifact,
    setFullscreen: setArtifactsFullscreen,
    fullscreen,
  } = useArtifacts();
  const { threadId, isNewThread, setIsNewThread, isMock, showWelcomeStyle } =
    useThreadChat();

  // 新逻辑：历史渲染和新会话仅由路由 /chats/new 控制，不再读取 isnew/is_chatting 参数。
  const shouldRenderHistory = !showWelcomeStyle;
  const safeThreadId = useMemo(() => {
    if (!threadId || threadId === "new") {
      return undefined;
    }
    return threadId;
  }, [threadId]);
  // `/new` + `thread_id` now reuses the pre-created thread, instead of creating
  // a new session on first submit.
  const createNewSession = useMemo(
    () => isNewThread && !safeThreadId,
    [isNewThread, safeThreadId],
  );
  const [isThreadInitReady, setIsThreadInitReady] = useState(false);

  const streamThreadId = useMemo(() => {
    if (!safeThreadId) {
      return undefined;
    }
    // In /new flow, defer history loading until thread init is finished:
    // delete -> create -> history.
    if (isNewThread && !isThreadInitReady) {
      return undefined;
    }
    return safeThreadId;
  }, [isNewThread, isThreadInitReady, safeThreadId]);
  const apiClient = useMemo(() => getAPIClient(isMock), [isMock]);
  const warnedMissingThreadIdRef = useRef(false);
  const initializedThreadRef = useRef<string | null>(null);
  const threadInitPromiseRef = useRef<Promise<void> | null>(null);

  const { showNotification } = useNotification();
  const currentSlogan = motivationSlogans[
    sloganIndex % motivationSlogans.length
  ] ?? {
    text: t.chatPage.defaultSlogan,
    color: "var(--color-ws-fg-primary)",
  };
  const tickerCharacterList = useMemo(() => {
    const seen = new Set<string>();
    const uniqueChars: string[] = [];

    for (const slogan of motivationSlogans) {
      for (const char of slogan.text) {
        if (seen.has(char)) continue;
        seen.add(char);
        uniqueChars.push(char);
      }
    }

    return uniqueChars.join("");
  }, []);

  useEffect(() => {
    if (motivationSlogans.length <= 1) return;

    const timer = window.setInterval(
      () => {
        setSloganIndex((prev) => (prev + 1) % motivationSlogans.length);
      },
      10 * 60 * 1000,
    );

    return () => window.clearInterval(timer);
  }, []);

  useEffect(() => {
    if (!isNewThread) {
      warnedMissingThreadIdRef.current = false;
      setIsThreadInitReady(true);
      return;
    }
    if (!safeThreadId) {
      if (!warnedMissingThreadIdRef.current) {
        warnedMissingThreadIdRef.current = true;
        toast.error(t.chatPage.missingThreadIdForCreate);
      }
      setIsThreadInitReady(false);
      return;
    }
    warnedMissingThreadIdRef.current = false;
    if (initializedThreadRef.current === safeThreadId) return;
    initializedThreadRef.current = safeThreadId;
    setIsThreadInitReady(false);

    const initPromise = apiClient.threads
      .delete(safeThreadId)
      .catch(() => undefined)
      .then(() =>
        apiClient.threads.create({
          threadId: safeThreadId,
          ifExists: "do_nothing",
        }),
      )
      .then(() => {
        setIsThreadInitReady(true);
      })
      .catch(() => {
        initializedThreadRef.current = null;
        setIsThreadInitReady(false);
        toast.error(t.chatPage.createSessionFailed);
      });

    threadInitPromiseRef.current = initPromise;
    void initPromise.finally(() => {
      if (threadInitPromiseRef.current === initPromise) {
        threadInitPromiseRef.current = null;
      }
    });
  }, [
    apiClient,
    isNewThread,
    safeThreadId,
    t.chatPage.createSessionFailed,
    t.chatPage.missingThreadIdForCreate,
  ]);

  // 监听宿主页 selectedSkill 消息
  const {
    skillError: selectedSkillError,
    clearSkillError: clearSelectedSkillError,
    isBootstrapping: isSelectedSkillBootstrapping,
  } = useSelectedSkillListener({ threadId: safeThreadId ?? null });
  // 对话行为控制器
  const [thread, sendMessage, isUploading] = useThreadStream({
    threadId: streamThreadId,
    context: settings.context,
    createNewSession,
    isMock,
    // 发送消息后跳转的逻辑
    onStart: (currentThreadId) => {
      setIsNewThread(false);
      // if (!shouldStayOnNewRoute) {
      // Keep /new in history so router.back() can return to it.
      router.replace(`/workspace/chats/${currentThreadId}?is_chatting=true`);
      // }
      // history.pushState(null, "", pathOfThread(currentThreadId));
    },
    onFinish: (state) => {
      if (document.hidden || !document.hasFocus()) {
        let body = t.chatPage.conversationFinished;
        const lastMessage = state.messages.at(-1);
        if (lastMessage) {
          const textContent = textOfMessage(lastMessage);
          if (textContent) {
            body =
              textContent.length > 200
                ? textContent.substring(0, 200) + "..."
                : textContent;
          }
        }
        showNotification(state.title, { body });
      }
    },
  });

  const title = useMemo(() => {
    const result = thread.values?.title ?? "";
    return result === "Untitled" ? "" : result;
  }, [thread.values?.title]);
  const sanitizedArtifacts = useMemo(
    () => sanitizeArtifactPaths(thread.values.artifacts),
    [thread.values.artifacts],
  );

  const [historyCutoff, setHistoryCutoff] = useState<number | null>(null);

  useEffect(() => {
    if (shouldRenderHistory) {
      setHistoryCutoff(null);
      return;
    }
    // Welcome 态下、未提交前，把当前已有消息都当作“历史”切掉。
    // 这样即使历史消息是后续异步补齐，也不会重新露出。
    setHistoryCutoff((prev) => {
      const next = thread.messages.length;
      if (prev === null) return next;
      return next > prev ? next : prev;
    });
  }, [
    historyCutoff,
    shouldRenderHistory,
    thread.isThreadLoading,
    thread.messages.length,
  ]);

  useEffect(() => {
    const pageTitle = isNewThread
      ? t.pages.newChat
      : thread.values?.title && thread.values.title !== "Untitled"
        ? thread.values.title
        : t.pages.untitled;
    if (thread.isThreadLoading) {
      document.title = `${t.common.loading} - ${t.pages.appName}`;
    } else {
      document.title = `${pageTitle} - ${t.pages.appName}`;
    }
  }, [
    isNewThread,
    t.common.loading,
    t.pages.newChat,
    t.pages.untitled,
    t.pages.appName,
    thread.values?.title,
    thread.isThreadLoading,
  ]);

  const [autoSelectFirstArtifact, setAutoSelectFirstArtifact] = useState(true);
  useEffect(() => {
    setArtifacts(sanitizedArtifacts);
    if (
      env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" &&
      autoSelectFirstArtifact
    ) {
      if (sanitizedArtifacts.length > 0) {
        setAutoSelectFirstArtifact(false);
        selectArtifact(sanitizedArtifacts[0]!);
      }
    }
  }, [
    autoSelectFirstArtifact,
    sanitizedArtifacts,
    selectArtifact,
    setArtifacts,
  ]);

  const artifactPanelOpen = useMemo(() => {
    if (env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true") {
      return artifactsOpen && artifacts?.length > 0;
    }
    return artifactsOpen;
  }, [artifactsOpen, artifacts]);

  const todoListCollapsed = true;
  const [showExitDialog, setShowExitDialog] = useState(false);
  const isStreaming = isUploading || thread.isLoading;
  const handleSubmit = useCallback(
    async (message: Parameters<typeof sendMessage>[1]) => {
      if (isSelectedSkillBootstrapping) {
        return;
      }
      if (isNewThread && !safeThreadId) {
        toast.error(t.chatPage.missingThreadIdForSend);
        return;
      }
      if (isNewThread && safeThreadId) {
        await threadInitPromiseRef.current;
      }
      if (isNewThread && safeThreadId && !isThreadInitReady) {
        return;
      }
      if (safeThreadId && (isNewThread || showWelcomeStyle)) {
        router.replace(`/workspace/chats/${safeThreadId}?is_chatting=true`);
      }
      void sendMessage(safeThreadId, message);
    },
    [
      isNewThread,
      isThreadInitReady,
      isSelectedSkillBootstrapping,
      router,
      safeThreadId,
      sendMessage,
      showWelcomeStyle,
      t.chatPage.missingThreadIdForSend,
    ],
  );
  const handleStop = useCallback(async () => {
    await thread.stop();
  }, [thread]);

  return (
    <ThreadContext.Provider value={{ threadId, thread }}>
      <div
        className={cn(
          "m-auto flex h-screen min-h-svh overflow-hidden rounded-t-[20px] transition-[width] duration-300 ease-in-out",
          artifactsOpen ? "w-full" : "w-[70%]",
        )}
      >
        <div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
          <div
            className={cn(
              "relative overflow-hidden rounded-t-[20px] transition-all duration-300 ease-in-out",
              artifactPanelOpen ? "w-[50%]" : "w-full",
              fullscreen && "hidden",
            )}
          >
            <div className="relative flex size-full min-h-0 justify-between rounded-t-[20px]">
              <header
                className={cn(
                  "bg-background absolute top-0 right-0 left-0 z-30 mx-4 grid h-[58px] shrink-0 grid-cols-3 items-center border-b transition-all duration-300 ease-in-out",
                  showWelcomeStyle ? "hidden" : "",
                )}
              >
                <div className="flex items-center justify-start overflow-hidden text-sm font-medium">
                  <Button
                    size="sm"
                    variant="ghost"
                    className="px-[10px] py-[5px] text-sm font-medium text-ws-base-1 hover:text-ws-base-1/80"
                    disabled={isStreaming}
                    onClick={() => {
                      sendToParent({
                        type: POST_MESSAGE_TYPES.IS_CHATTING,
                        isChatting: false,
                      });
                      router.replace(`/workspace/chats/${threadId}?is_chatting=false`)
                    }
                    }
                  >
                    <svg
                      width="20"
                      height="20"
                      viewBox="0 0 20 20"
                      fill="none"
                      xmlns="http://www.w3.org/2000/svg"
                    >
                      <path
                        d="M3.5 10H13.25H15.6875H16.5M3.5 10L7.5625 6M3.5 10L7.5625 14"
                        className="text-ws-text-muted"
                        stroke="currentColor"
                        strokeWidth="1.5"
                        strokeLinecap="round"
                        strokeLinejoin="round"
                      />
                    </svg>
                  </Button>
                </div>
                <div
                  className="flex items-center justify-center overflow-hidden text-sm font-bold font-medium whitespace-nowrap text-ws-fg-primary"
                  style={{
                    color: currentSlogan.color,
                  }}
                >
                  {/* threadTitle={title} */}
                  {title !== "Untitled" && (
                    // <ThreadTitle threadId={threadId} threadTitle={'来，一起学习工作吧'} />
                    <Ticker
                      value={currentSlogan.text}
                      duration={800}
                      easing="easeInOut"
                      charWidth={1}
                      characterLists={tickerCharacterList}
                    />
                  )}
                </div>
                <div className="flex items-center justify-end gap-2 overflow-hidden">
                  {/* 取消TodoList */}
                  {/* <DevTodoList
                    className="bg-ws-surface-base"
                    todos={thread.values.todos ?? []}
                    hidden={
                      !thread.values.todos || thread.values.todos.length === 0
                    }
                    trigger={
                      <Button
                        size="sm"
                        variant="ghost"
                        className="h-full px-[10px] py-[5px] text-sm font-medium text-ws-base-1 hover:text-ws-base-1"
                      >
                        <ListTodoIcon className="size-4" /> To-dos
                      </Button>
                    }
                  /> */}
                  <Tooltip content={t.common.resetThread}>
                    <Button
                      size="sm"
                      variant="ghost"
                      className="h-full px-[10px] py-[5px] text-sm font-medium text-ws-base-1 hover:text-ws-base-1"
                      disabled={isStreaming}
                      onClick={() => setShowExitDialog(true)}
                    >
                      <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
                        <path d="M2 4H6M16 4H12M6 4H12M6 4C6 2.89543 6.89543 2 8 2H10C11.1046 2 12 2.89543 12 4M4 6V14C4 15.1046 4.89543 16 6 16H12C13.1046 16 14 15.1046 14 14V6M7 8V13M11 8V13" stroke="#150033" strokeLinecap="round" />
                      </svg>
                      {t.common.resetThread}
                    </Button>
                  </Tooltip>

                  {artifacts?.length > 0 && !artifactsOpen && (
                    <Tooltip content={t.chatPage.viewArtifactsTooltip}>
                      <Button
                        data-testid="artifacts-open-button"
                        className="text-ws-base-1 hover:text-ws-base-1/80"
                        variant="ghost"
                        onClick={() => {
                          setArtifactsOpen(true);
                          setSidebarOpen(false);
                        }}
                      >
                        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
                          <path d="M16 7V4C16 2.89543 15.1046 2 14 2H4C2.89543 2 2 2.89543 2 4V14C2 15.1046 2.89543 16 4 16H9" stroke="#150033" strokeLinecap="round" />
                          <path d="M5 5H9M5 8H7" stroke="#150033" strokeLinecap="round" strokeLinejoin="round" />
                          <circle cx="11.5" cy="10.5" r="3" stroke="#150033" />
                          <path d="M15.5 14.5L14 13" stroke="#150033" strokeLinecap="round" strokeLinejoin="round" />
                        </svg>
                        {t.common.artifacts}
                      </Button>
                    </Tooltip>
                  )}
                </div>
              </header>
              <main
                className={cn(
                  "flex min-h-0 max-w-full grow flex-col",
                  showWelcomeStyle ? "bg-ws-surface-base" : "bg-background",
                )}
              >
                <div className="flex size-full justify-center">
                  <MessageList
                    className={cn(
                      "size-full",
                      !showWelcomeStyle && "pt-[58px]",
                    )}
                    threadId={threadId}
                    thread={thread}
                    messagesOverride={
                      shouldRenderHistory
                        ? undefined
                        : historyCutoff === null
                          ? []
                          : thread.messages.slice(historyCutoff)
                    }
                    paddingBottom={todoListCollapsed ? 160 : 280}
                    showScrollToBottomButton={!showWelcomeStyle}
                    scrollButtonClassName="bottom-[112px]"
                  />
                </div>
              </main>
            </div>
          </div>
          <div
            className={cn(
              "bg-background ml-[20px] rounded-t-[20px] transition-all duration-300 ease-in-out",
              !artifactsOpen && "opacity-0",
              artifactPanelOpen
                ? fullscreen
                  ? "ml-0 w-full"
                  : "w-[50%]"
                : "w-0",
            )}
          >
            <div
              className={cn(
                "h-full w-full transition-transform duration-300 ease-in-out",
                showWelcomeStyle ? "translate-x-0" : "",
                artifactPanelOpen ? "translate-x-0" : "translate-x-full",
              )}
            >
              {selectedArtifact ? (
                <ArtifactFileDetail
                  className="size-full"
                  filepath={selectedArtifact}
                  threadId={threadId}
                />
              ) : (
                <div className="relative flex size-full justify-center px-[20px]">
                  <div className="z-30"></div>
                  {sanitizedArtifacts.length === 0 ? (
                    <ConversationEmptyState
                      icon={<FilesIcon />}
                      title={t.chatPage.noArtifactSelectedTitle}
                      description={t.chatPage.noArtifactSelectedDescription}
                    />
                  ) : (
                    <div className="flex size-full max-w-(--container-width-sm) flex-col justify-center">
                      <header className="flex shrink-0 items-center justify-between border-b">
                        <h2 className="h-[58px] text-sm leading-[58px] font-bold text-ws-fg-primary">
                          <span>{t.common.artifacts}</span>
                        </h2>
                        <Button
                          data-testid="artifacts-panel-close"
                          size="icon-sm"
                          variant="ghost"
                          onClick={() => {
                            setArtifactsOpen(false);
                          }}
                        >
                          <XIcon />
                        </Button>
                      </header>
                      <main className="min-h-0 grow overflow-auto">
                        <ArtifactFileList
                          className="mb-[207px] max-w-(--container-width-sm) pt-[20px]"
                          files={sanitizedArtifacts}
                          threadId={threadId}
                        />
                      </main>
                    </div>
                  )}
                </div>
              )}
            </div>
          </div>
        </div>

        {/* Fixed 底部居中输入框容器 */}
        <div
          className={cn(
            "pointer-events-none fixed right-0 bottom-3 left-0 z-30 flex justify-center px-4",
            "transition-all duration-300 ease-in-out",
            fullscreen ? "hidden" : "",
          )}
        >
          <div
            className={cn(
              "pointer-events-auto relative w-full max-w-[720px]",
              showWelcomeStyle && "-translate-y-[calc(50vh-96px)]",
            )}
          >
            {!(showWelcomeStyle && thread.isThreadLoading) ? (
              <>
                <InputBox
                  className={cn("w-full rounded-[20px] bg-ws-surface-elevated")}
                  threadId={threadId}
                  showWelcomeStyle={showWelcomeStyle}
                  autoFocus={showWelcomeStyle}
                  status={
                    thread.error
                      ? "error"
                      : isUploading || thread.isLoading
                        ? "streaming"
                        : "ready"
                  }
                  context={settings.context}
                  extraHeader={
                    <div className="flex flex-col gap-4">
                      {showWelcomeStyle && <Welcome mode={settings.context.mode} />}
                    </div>
                  }
                  disabled={
                    env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ||
                    isSelectedSkillBootstrapping ||
                    isUploading ||
                    (isNewThread && !safeThreadId)
                  }
                  onContextChange={(context) => setSettings("context", context)}
                  onSubmit={handleSubmit}
                  onStop={handleStop}
                />
              </>
            ) : (
              // <InputBoxSkeleton />
              ""
            )}

            {/* {isSelectedSkillBootstrapping && (
              <div className="text-muted-foreground w-full translate-y-8 text-center text-xs">
                正在初始化 Skill 文件...
              </div>
            )} */}
            {env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && (
              <div className="text-muted-foreground/67 w-full translate-y-12 text-center text-xs">
                {t.common.notAvailableInDemoMode}
              </div>
            )}
          </div>
        </div>

        {/* 退出确认对话框 */}
        <DevDialog open={showExitDialog} onOpenChange={setShowExitDialog}>
          <DevDialogContent>
            <DevDialogHeader>
              <DevDialogTitle>{t.chatPage.exitDialogTitle}</DevDialogTitle>
            </DevDialogHeader>
            <p className="text-muted-foreground text-sm">
              {t.chatPage.exitDialogDescription}
            </p>
            <DevDialogFooter>
              <Button
                className="w-full bg-ws-surface-subtle hover:bg-ws-interactive-primary hover:text-primary-foreground"
                variant="ghost"
                onClick={() => setShowExitDialog(false)}
              >
                {t.common.cancel}
              </Button>
              <Button
                className="w-full bg-ws-surface-subtle hover:bg-ws-interactive-primary hover:text-primary-foreground"
                variant="ghost"
                onClick={async () => {
                  // 如果正在生成，先终止再退出
                  if (thread.isLoading) {
                    await handleStop();
                  }
                  setShowExitDialog(false);
                  sendToParent({
                    type: POST_MESSAGE_TYPES.IS_CHATTING,
                    isChatting: false,
                  });
                  // 始终复用 query 中的 thread_id。
                  const nextQuery = new URLSearchParams();
                  if (threadId && threadId !== "new") {
                    nextQuery.set("thread_id", threadId);
                  }
                  // /workspace/chats/${threadId}?is_chatting=false
                  router.replace(
                    `/workspace/chats/new?thread_id=${threadId}`,
                  );
                }}
              >
                {t.chatPage.exitDialogConfirm}
              </Button>
            </DevDialogFooter>
          </DevDialogContent>
        </DevDialog>

        {/* selectedSkill 失败：错误弹窗 */}
        <DevDialog
          open={!!selectedSkillError}
          onOpenChange={(open) => {
            if (!open) clearSelectedSkillError();
          }}
        >
          <DevDialogContent>
            <DevDialogHeader>
              <DevDialogTitle>
                ⚠️{" "}
                {selectedSkillError?.title ??
                  t.chatPage.selectedSkillLoadFailed}
              </DevDialogTitle>
            </DevDialogHeader>
            <p className="text-muted-foreground text-sm">
              {selectedSkillError?.message ?? t.chatPage.unknownErrorRetry}
            </p>
            <DevDialogFooter singleColumn>
              <Button
                className="w-full bg-ws-surface-subtle hover:bg-ws-interactive-primary hover:text-primary-foreground"
                variant="ghost"
                onClick={clearSelectedSkillError}
              >
                {t.common.close}
              </Button>
            </DevDialogFooter>
          </DevDialogContent>
        </DevDialog>

        {/* MARK: 开发测试：iframe 通信功能测试面板 */}
        {/* {process.env.NODE_ENV !== "production" && <IframeTestPanel />} */}
      </div>
    </ThreadContext.Provider>
  );
}
