"use client";

import {
  Bolt,
  CheckCircle2,
  ChevronsUpDown,
  ChevronLeft,
  Gauge,
  History,
  Plus,
  SendHorizontal,
  Sparkles
} from "lucide-react";
import { AnimatePresence, MotionConfig, motion } from "motion/react";
import { useEffect, useMemo, useRef, useState } from "react";
import { useStickToBottomContext } from "use-stick-to-bottom";
import { Agent, AgentContent } from "@/shared/ai-elements/agent";
import {
  Conversation,
  ConversationContent,
  ConversationScrollButton
} from "@/shared/ai-elements/conversation";
import type { PersonaState } from "@/shared/ai-elements/persona";
import { Message, MessageContent } from "@/shared/ai-elements/message";
import {
  PromptInput,
  PromptInputBody,
  PromptInputFooter,
  PromptInputSubmit,
  PromptInputTextarea
} from "@/shared/ai-elements/prompt-input";
import { Suggestion, Suggestions } from "@/shared/ai-elements/suggestion";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuTrigger
} from "@/shared/ui/dropdown-menu";
import { Button } from "@/shared/ui/button";
import { cn } from "@/lib/utils";
import {
  MAP_CONTROL_SURFACE_CLASS_NAME,
  MAP_MAJOR_PANEL_RADIUS_CLASS_NAME
} from "@/features/map/core/components/map-control-styles";
import type { AgentChatSessionSummary } from "../api/client";
import { AgentPersona } from "./agent-persona";
import {
  agentLayoutTransition,
  agentPanelItemVariants,
  agentPanelListVariants,
  agentPanelSectionVariants
} from "./agent-motion";
import { AgentHistoryPanel } from "./agent-history-panel";
import { AgentMessageDetails, AgentMessageProgress } from "./agent-message-details";
import { AgentOperationalBrief } from "./agent-operational-brief";
import { AgentUiEnvelopeRenderer } from "./agent-ui-envelope-renderer";
import { StreamingTokenResponse } from "./streaming-token-response";
import type {
  AgentChatMessage,
  AgentModelOption,
  AgentPermissionReply,
  AgentPermissionRequest,
  AgentQuestionRequest,
  AgentStreamRenderState,
  AgentUiResult
} from "../types";

type AgentCommandPanelProps = {
  collapsing?: boolean;
  personaState?: PersonaState;
  sessionTitle?: string;
  sessionHistory?: AgentChatSessionSummary[];
  sessionHistoryLoading?: boolean;
  activeSessionId?: string | null;
  statusLabel?: string;
  streaming?: boolean;
  messages?: AgentChatMessage[];
  streamRenderState?: AgentStreamRenderState;
  modelOptions?: AgentModelOption[];
  selectedModel?: string;
  uiResults?: AgentUiResult[];
  onCollapse: () => void;
  onRefreshHistory?: () => Promise<void> | void;
  onStartNewSession?: () => Promise<void> | void;
  onLoadHistorySession?: (sessionId: string) => Promise<void> | void;
  onRenameHistorySession?: (sessionId: string, title: string) => Promise<void> | void;
  onDeleteHistorySession?: (sessionId: string) => Promise<void> | void;
  onSelectModel?: (model: string) => void;
  onSubmitPrompt: (prompt: string) => Promise<void> | void;
  onStopPrompt?: () => void;
  onReplyPermission?: (permission: AgentPermissionRequest, reply: AgentPermissionReply) => Promise<void> | void;
  onReplyQuestion?: (request: AgentQuestionRequest, answers: string[][]) => Promise<void> | void;
  onRejectQuestion?: (request: AgentQuestionRequest) => Promise<void> | void;
};

const AGENT_PROMPT_SUGGESTIONS = [
  "当前监测数据质量如何，是否存在缺失、失真或可信度不足的问题？",
  "当前监测数据中有哪些异常问题需要重点关注？",
  "当前管网哪些区域可能存在雨污混接，判断依据是什么？"
] as const;

export function AgentCommandPanel({
  collapsing = false,
  personaState,
  sessionTitle = "新对话",
  sessionHistory = [],
  sessionHistoryLoading = false,
  activeSessionId,
  statusLabel = "Agent 后端连接中",
  streaming = false,
  messages = [],
  streamRenderState = {},
  modelOptions = [],
  selectedModel = "",
  uiResults = [],
  onCollapse,
  onRefreshHistory,
  onStartNewSession,
  onLoadHistorySession,
  onRenameHistorySession,
  onDeleteHistorySession,
  onSelectModel,
  onSubmitPrompt,
  onStopPrompt,
  onReplyPermission,
  onReplyQuestion,
  onRejectQuestion
}: AgentCommandPanelProps) {
  const [prompt, setPrompt] = useState("");
  const [historyOpen, setHistoryOpen] = useState(false);
  const [loadingSessionId, setLoadingSessionId] = useState<string | null>(null);
  const [scrollRequestId, setScrollRequestId] = useState(0);
  const trimmedPrompt = prompt.trim();

  useEffect(() => {
    if (historyOpen) {
      void Promise.resolve(onRefreshHistory?.());
    }
  }, [historyOpen, onRefreshHistory]);

  const submitPrompt = (nextPrompt: string) => {
    if (!nextPrompt || streaming) {
      return;
    }
    setPrompt("");
    setScrollRequestId((requestId) => requestId + 1);
    void Promise.resolve(onSubmitPrompt(nextPrompt)).catch(() => {
      setPrompt(nextPrompt);
    });
  };

  return (
    <MotionConfig reducedMotion="user">
      <aside
        className={cn(
          "pointer-events-auto flex h-full min-h-0 w-full flex-col overflow-hidden",
          MAP_MAJOR_PANEL_RADIUS_CLASS_NAME,
          MAP_CONTROL_SURFACE_CLASS_NAME,
          collapsing ? "agent-panel-collapse" : "agent-panel-enter"
        )}
      >
      <Agent className="flex h-full min-h-0 flex-col border-0 bg-transparent">
        <div className="agent-panel-band border-b border-white/70">
          <div className="flex items-center justify-between gap-3 px-4 py-3">
            <div className="flex min-w-0 flex-1 items-center gap-3 text-slate-900">
              <AgentPersona
                className="h-[72px] w-[72px]"
                fallbackClassName="h-[72px] w-[72px] rounded-2xl"
                state={personaState}
                statusLabel={statusLabel}
                streaming={streaming}
              />
              <div className="min-w-0 flex-1 space-y-2">
                <div className="min-w-0">
                  <div className="flex min-w-0 items-center gap-2">
                    <p className="shrink-0 text-xs font-semibold uppercase text-slate-500">对话主题</p>
                    <span className="inline-flex h-6 shrink-0 items-center gap-1.5 rounded-lg border border-slate-200/70 bg-[var(--glass-readable)] px-2 text-xs font-semibold text-slate-600">
                      <span className={cn("h-1.5 w-1.5 rounded-full", getAgentConnectionClassName(statusLabel))} />
                      {getAgentConnectionLabel(statusLabel)}
                    </span>
                  </div>
                  <p className="truncate text-sm font-semibold text-slate-950" title={sessionTitle}>
                    {sessionTitle}
                  </p>
                </div>
              </div>
            </div>
            <button
              type="button"
              aria-label="新建 Agent 对话"
              title="新建 Agent 对话"
              onClick={() => {
                setHistoryOpen(false);
                setPrompt("");
                void Promise.resolve(onStartNewSession?.());
              }}
              className="agent-panel-icon-button grid h-9 w-9 shrink-0 place-items-center rounded-xl text-slate-600 transition"
            >
              <Plus size={17} aria-hidden="true" />
            </button>
            <button
              type="button"
              aria-label="打开 Agent 历史记录"
              title="打开 Agent 历史记录"
              onClick={() => setHistoryOpen((open) => !open)}
              className={cn(
                "agent-panel-icon-button grid h-9 w-9 shrink-0 place-items-center rounded-xl text-slate-600 transition",
                historyOpen && "border-blue-200 bg-blue-50 text-blue-700"
              )}
            >
              <History size={17} aria-hidden="true" />
            </button>
            <button
              type="button"
              aria-label="折叠 Agent 面板"
              title="折叠 Agent 面板"
              onClick={onCollapse}
              className="agent-panel-icon-button grid h-9 w-9 shrink-0 place-items-center rounded-xl text-slate-600 transition"
            >
              <ChevronLeft size={17} aria-hidden="true" />
            </button>
          </div>
          <AnimatePresence initial={false}>
            {historyOpen ? (
              <motion.div
                key="history"
                initial="initial"
                animate="animate"
                exit="exit"
                variants={agentPanelSectionVariants}
                style={{ overflow: "hidden" }}
              >
                <AgentHistoryPanel
                  activeSessionId={activeSessionId}
                  loadingSessionId={loadingSessionId}
                  loading={sessionHistoryLoading}
                  sessions={sessionHistory}
                  onRefresh={onRefreshHistory}
                  onSelectSession={(nextSessionId) => {
                    setLoadingSessionId(nextSessionId);
                    void Promise.resolve(onLoadHistorySession?.(nextSessionId))
                      .then(() => setHistoryOpen(false))
                      .finally(() => setLoadingSessionId(null));
                  }}
                  onRenameSession={onRenameHistorySession}
                  onDeleteSession={onDeleteHistorySession}
                />
              </motion.div>
            ) : null}
          </AnimatePresence>
          <AnimatePresence initial={false}>
            {messages.length > 0 || streaming ? (
              <motion.div
                key="operational-brief"
                className="space-y-2 px-3 pb-3"
                initial="initial"
                animate="animate"
                exit="exit"
                variants={agentPanelSectionVariants}
                style={{ overflow: "hidden" }}
              >
                <AgentOperationalBrief
                  messages={messages}
                  statusLabel={statusLabel}
                  streaming={streaming}
                  onSubmitCommand={submitPrompt}
                />
              </motion.div>
            ) : null}
          </AnimatePresence>
        </div>

        <AgentContent className="flex min-h-0 flex-1 flex-col gap-0 p-0">
          <Conversation className="agent-panel-conversation min-h-0">
            <ConversationContent className="gap-4 p-3">
              <AnimatePresence mode="popLayout" initial={false}>
                {messages.length > 0 ? (
                  <BackendMessageList
                    key="messages"
                    messages={messages}
                    streaming={streaming}
                    streamRenderState={streamRenderState}
                    onReplyPermission={onReplyPermission}
                    onReplyQuestion={onReplyQuestion}
                    onRejectQuestion={onRejectQuestion}
                  />
                ) : (
                  <motion.div
                    key="empty"
                    className="w-full"
                    initial="initial"
                    animate="animate"
                    exit="exit"
                    variants={agentPanelItemVariants}
                  >
                    <AgentEmptyState statusLabel={statusLabel} />
                  </motion.div>
                )}
              </AnimatePresence>

              <AgentUiEnvelopeRenderer results={uiResults} />
            </ConversationContent>
            <AgentConversationScrollManager
              activeSessionId={activeSessionId}
              messages={messages}
              scrollRequestId={scrollRequestId}
              streaming={streaming}
              uiResults={uiResults}
            />
            <ConversationScrollButton className="bottom-3" />
          </Conversation>
        </AgentContent>

        <div className="agent-panel-band space-y-2 border-t border-white/70 p-3">
          <Suggestions aria-label="推荐问题" role="group" className="px-0.5">
            {AGENT_PROMPT_SUGGESTIONS.map((suggestion) => (
              <Suggestion
                key={suggestion}
                suggestion={suggestion}
                disabled={streaming}
                onClick={submitPrompt}
                className="max-w-full border-slate-200/80 bg-[var(--glass-readable)] text-slate-600 shadow-none hover:border-blue-200 hover:bg-blue-50 hover:text-blue-700"
              >
                <span className="truncate">{suggestion}</span>
              </Suggestion>
            ))}
          </Suggestions>
          <PromptInput
            className="agent-panel-control rounded-2xl shadow-sm"
            onSubmit={(message) => {
              const nextPrompt = message.text.trim();
              if (nextPrompt) {
                submitPrompt(nextPrompt);
              }
            }}
          >
            <PromptInputBody>
              <PromptInputTextarea
                value={prompt}
                onChange={(event) => setPrompt(event.currentTarget.value)}
                className="min-h-14 text-sm"
                placeholder="输入调度问题，Agent 将通过后端会话流式响应"
              />
            </PromptInputBody>
            <PromptInputFooter>
              <span className="min-w-0 flex-1 truncate text-xs text-slate-500" title={statusLabel}>
                {statusLabel}
              </span>
              <div className="flex shrink-0 items-center gap-1.5">
                <AgentModelSelect
                  models={modelOptions}
                  value={selectedModel}
                  onValueChange={onSelectModel}
                />
                <PromptInputSubmit
                  aria-label={streaming ? "停止 Agent 指令" : "发送 Agent 指令"}
                  title={streaming ? "停止 Agent 指令" : "发送 Agent 指令"}
                  className="agent-panel-primary-icon"
                  disabled={!streaming && !trimmedPrompt}
                  status={streaming ? "streaming" : "ready"}
                  onStop={onStopPrompt}
                >
                  {streaming ? undefined : <SendHorizontal size={15} aria-hidden="true" />}
                </PromptInputSubmit>
              </div>
            </PromptInputFooter>
          </PromptInput>
        </div>
      </Agent>
      </aside>
    </MotionConfig>
  );
}

type AgentConversationScrollSnapshot = {
  activeSessionId?: string | null;
  lastUserMessageId: string | null;
  scrollRequestId: number;
  signature: string;
};

function AgentConversationScrollManager({
  activeSessionId,
  messages,
  scrollRequestId,
  streaming,
  uiResults
}: {
  activeSessionId?: string | null;
  messages: AgentChatMessage[];
  scrollRequestId: number;
  streaming: boolean;
  uiResults: AgentUiResult[];
}) {
  const { escapedFromLock, isAtBottom, scrollToBottom, state } = useStickToBottomContext();
  const previousSnapshotRef = useRef<AgentConversationScrollSnapshot | null>(null);
  const wasPinnedToBottom = !escapedFromLock && (isAtBottom || state.isAtBottom || state.isNearBottom);
  const snapshot = useMemo<AgentConversationScrollSnapshot>(
    () => ({
      activeSessionId,
      lastUserMessageId: getLastUserMessageId(messages),
      scrollRequestId,
      signature: createAgentConversationScrollSignature(messages, streaming, uiResults)
    }),
    [activeSessionId, messages, scrollRequestId, streaming, uiResults]
  );

  useEffect(() => {
    const previousSnapshot = previousSnapshotRef.current;
    previousSnapshotRef.current = snapshot;

    const forceScroll =
      !previousSnapshot ||
      previousSnapshot.activeSessionId !== snapshot.activeSessionId ||
      previousSnapshot.scrollRequestId !== snapshot.scrollRequestId ||
      (Boolean(snapshot.lastUserMessageId) && previousSnapshot.lastUserMessageId !== snapshot.lastUserMessageId);
    const contentChanged = !previousSnapshot || previousSnapshot.signature !== snapshot.signature;

    if (!forceScroll && (!contentChanged || !wasPinnedToBottom)) {
      return;
    }

    const animationFrameId = window.requestAnimationFrame(() => {
      void scrollToBottom({
        animation: "instant",
        ignoreEscapes: forceScroll,
        wait: !forceScroll
      });
    });

    return () => window.cancelAnimationFrame(animationFrameId);
  }, [scrollToBottom, snapshot, wasPinnedToBottom]);

  return null;
}

function getLastUserMessageId(messages: AgentChatMessage[]) {
  for (let index = messages.length - 1; index >= 0; index -= 1) {
    if (messages[index].role === "user") {
      return messages[index].id;
    }
  }

  return null;
}

function createAgentConversationScrollSignature(
  messages: AgentChatMessage[],
  streaming: boolean,
  uiResults: AgentUiResult[]
) {
  return [
    streaming ? "streaming" : "idle",
    messages.map(createAgentMessageScrollSignature).join("|"),
    uiResults.map((result) => result.id).join("|")
  ].join("#");
}

function createAgentMessageScrollSignature(message: AgentChatMessage) {
  return [
    message.id,
    message.role,
    message.content.length,
    message.progress?.map((item) => `${item.id}:${item.status}:${item.title.length}:${item.detail?.length ?? 0}`).join(",") ?? "",
    message.todos?.todos.map((todo) => `${todo.id}:${todo.status}:${todo.content.length}`).join(",") ?? "",
    message.permissions?.map((permission) => `${permission.requestId}:${permission.status}:${permission.error?.length ?? 0}`).join(",") ?? "",
    message.questions?.map((question) => `${question.requestId}:${question.status}:${question.error?.length ?? 0}`).join(",") ?? ""
  ].join(":");
}

function AgentModelSelect({
  models,
  value,
  onValueChange
}: {
  models: AgentModelOption[];
  value: string;
  onValueChange?: (model: string) => void;
}) {
  const selectedModel = models.find((model) => model.id === value) ?? models[0];

  if (!models.length) {
    return (
      <span className="shrink-0 rounded-md px-2 py-1 text-xs font-medium text-slate-500">
        模型
      </span>
    );
  }

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button
          type="button"
          variant="outline"
          className="h-8 w-[126px] shrink-0 justify-between rounded-md border-transparent bg-transparent px-2 text-xs text-slate-600 shadow-none hover:bg-white/80"
          disabled={!onValueChange}
          aria-label="选择 Agent 模型"
        >
          <span className="flex min-w-0 items-center gap-1.5">
            <ModelIcon model={selectedModel} size={13} />
            <span className="truncate">{selectedModel?.label ?? "模型"}</span>
          </span>
          <ChevronsUpDown size={13} className="shrink-0 opacity-50" aria-hidden="true" />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent
        side="top"
        align="end"
        sideOffset={8}
        className="agent-panel-control w-72 rounded-xl p-2 shadow-lg"
      >
        <DropdownMenuLabel className="px-2 pb-2 pt-1 text-xs text-slate-500">
          Agent 模型
        </DropdownMenuLabel>
        <div className="space-y-1">
          {models.map((model) => {
            const selected = model.id === selectedModel?.id;
            return (
              <DropdownMenuItem
                key={model.id}
                className={cn(
                  "rounded-lg border border-transparent px-2.5 py-2 focus:bg-slate-50",
                  selected && "glass-agent border-violet-200 focus:bg-violet-50"
                )}
                onSelect={() => onValueChange?.(model.id)}
              >
                <span className="grid h-8 w-8 shrink-0 place-items-center rounded-md bg-white text-slate-600 shadow-sm">
                  <ModelIcon model={model} size={15} />
                </span>
                <span className="min-w-0 flex-1">
                  <span className="block truncate text-sm font-semibold text-slate-800">{model.label}</span>
                  <span className="block truncate text-xs leading-4 text-slate-500">{model.description}</span>
                </span>
                {selected ? <CheckCircle2 size={15} className="text-violet-600" aria-hidden="true" /> : null}
              </DropdownMenuItem>
            );
          })}
        </div>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

function getAgentConnectionLabel(statusLabel: string) {
  return /失败|不可用|错误|降级/.test(statusLabel) ? "离线" : "在线";
}

function getAgentConnectionClassName(statusLabel: string) {
  return /失败|不可用|错误|降级/.test(statusLabel) ? "bg-red-500" : "bg-emerald-500";
}

function ModelIcon({ model, size }: { model?: AgentModelOption; size: number }) {
  return model?.icon === "bolt" ? (
    <Bolt size={size} className="text-amber-500" aria-hidden="true" />
  ) : (
    <Sparkles size={size} className="text-violet-500" aria-hidden="true" />
  );
}

function AgentEmptyState({ statusLabel }: { statusLabel: string }) {
  return (
    <Message from="assistant" className="max-w-full">
      <MessageContent className="agent-panel-message w-full rounded-2xl p-3 text-sm leading-6 text-slate-600 shadow-sm">
        <div className="flex items-center gap-2">
          <Gauge size={15} className="text-blue-600" aria-hidden="true" />
          <p className="font-semibold text-slate-900">等待调度任务</p>
        </div>
        <p className="mt-2">后端会话已接入，新的任务会在这里形成证据、计划、预览和确认记录。</p>
        <p className="mt-1 text-xs text-slate-500">{statusLabel}</p>
      </MessageContent>
    </Message>
  );
}

function BackendMessageList({
  messages,
  streaming,
  streamRenderState,
  onReplyPermission,
  onReplyQuestion,
  onRejectQuestion
}: {
  messages: AgentChatMessage[];
  streaming: boolean;
  streamRenderState: AgentStreamRenderState;
  onReplyPermission?: (permission: AgentPermissionRequest, reply: AgentPermissionReply) => Promise<void> | void;
  onReplyQuestion?: (request: AgentQuestionRequest, answers: string[][]) => Promise<void> | void;
  onRejectQuestion?: (request: AgentQuestionRequest) => Promise<void> | void;
}) {
  return (
    <motion.div className="flex w-full flex-col gap-4" variants={agentPanelListVariants} animate="animate">
      <AnimatePresence initial={false} mode="popLayout">
        {messages.map((message, index) => (
          <motion.div
            key={message.id}
            layout
            className={cn("w-full", message.role === "user" && "flex justify-end")}
            initial="initial"
            animate="animate"
            exit="exit"
            variants={agentPanelItemVariants}
            transition={agentLayoutTransition}
          >
            <Message
              from={message.role}
              className={message.role === "user" ? "max-w-[92%]" : "max-w-full"}
            >
              <MessageContent
                className={cn(
                  message.role === "user" && "bg-blue-600 px-3 py-2 text-white",
                  message.role === "assistant" && "agent-panel-message w-full rounded-2xl p-3 text-sm leading-6 text-slate-700 shadow-sm"
                )}
              >
                <div className="space-y-3">
                  {message.role === "assistant" ? (
                    <AgentMessageProgress
                      progress={message.progress}
                      running={streaming && index === messages.length - 1}
                    />
                  ) : null}
                  {message.content ? (
                    message.role === "assistant" ? (
                      <StreamingTokenResponse
                        className="leading-6"
                        content={message.content}
                        messageId={message.id}
                        streamDone={streamRenderState[message.id]?.done ?? !streaming}
                        streaming={streaming && index === messages.length - 1}
                      />
                    ) : (
                      <p className="whitespace-pre-wrap">{message.content}</p>
                    )
                  ) : streaming && index === messages.length - 1 ? (
                    <span className="text-slate-500">正在生成...</span>
                  ) : null}
                  {message.role === "assistant" ? (
                    <AgentMessageDetails
                      message={message}
                      onReplyPermission={onReplyPermission}
                      onReplyQuestion={onReplyQuestion}
                      onRejectQuestion={onRejectQuestion}
                    />
                  ) : null}
                </div>
              </MessageContent>
            </Message>
          </motion.div>
        ))}
      </AnimatePresence>
    </motion.div>
  );
}
