"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import {
  Command,
  DatabaseZap,
  FileSearch,
  Image,
  PanelRightClose,
  PanelRightOpen,
  RefreshCcw,
  Search,
  SendHorizontal,
  SquareStop,
  Trash2,
  type LucideIcon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Textarea } from "@/components/ui/textarea";
import { AiBridgePanel } from "./AiBridgePanel";
import { parseSseChunks, readAiPanelPrefs, type AiProvider, writeAiPanelPrefs } from "./panelShared";

type ChatMsg = { role: "user" | "assistant"; content: string };
type ToolLog =
  | { type: "tool_call"; id: string; tool: string; args: Record<string, unknown> }
  | { type: "tool_result"; id: string; tool: string; ok: boolean; ms: number; result: unknown }
  | { type: "error"; message: string };

type CapabilityItem = {
  title: string;
  description: string;
  icon: LucideIcon;
};

type ToolSetChip = {
  id: string;
  title: string;
  description: string;
};

const DEFAULT_PROMPT = "请给出 gemini-3 tokens 价格，并提供来源链接。";
const MIN_PANEL_AGENT_STEPS = 1;
const MAX_PANEL_AGENT_STEPS = 24;
const DEFAULT_PREFS = { provider: "online" as AiProvider, model: "", maxSteps: 10 };

const TOOLSET_CHIPS: ToolSetChip[] = [
  { id: "toolset.readonly", title: "联网检索", description: "SearxNG 可追溯来源" },
  { id: "toolset.rag_read", title: "LightRAG", description: "本地知识检索" },
  { id: "toolset.docs_read", title: "跨页面文档", description: "搜索与读取工作区文档" },
  { id: "toolset.media_read", title: "图片 OCR", description: "读取图片与附件文字" },
  { id: "toolset.slash_write", title: "斜杠命令", description: "受控执行写入动作" },
];

const CAPABILITY_ITEMS: CapabilityItem[] = [
  { title: "联网检索", description: "搜索公开网页并返回来源链接，适合查价格、规格、资料。", icon: Search },
  { title: "LightRAG", description: "结合知识库做语义检索与生成，适合已有资料沉淀场景。", icon: DatabaseZap },
  { title: "跨页面文档", description: "搜索并读取当前工作区中的文档内容，用于对比和归纳。", icon: FileSearch },
  { title: "图片 OCR", description: "若当前请求已带图片或附件，可读取其中的 OCR 文字内容。", icon: Image },
  { title: "斜杠命令", description: "执行受控写操作，例如创建文档或改名，需要明确确认。", icon: Command },
];

const formatJson = (value: unknown) => {
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return String(value);
  }
};

const isAbortLikeError = (error: unknown) => {
  return (
    (typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError") ||
    (error instanceof Error && error.name === "AbortError")
  );
};

const clampStep = (value: number) => Math.min(Math.max(value, MIN_PANEL_AGENT_STEPS), MAX_PANEL_AGENT_STEPS);

const getLogToneClass = (log: ToolLog) => {
  if (log.type === "error") return "border-red-500/30 bg-red-500/10";
  if (log.type === "tool_result") {
    return log.ok ? "border-emerald-500/20 bg-emerald-500/10" : "border-amber-500/25 bg-amber-500/10";
  }
  return "border-white/10 bg-white/[0.03]";
};

const getLogLabel = (log: ToolLog) => {
  if (log.type === "tool_call") return "工具请求";
  if (log.type === "tool_result") return log.ok ? "工具结果 · 成功" : "工具结果 · 失败";
  return "运行错误";
};

const getProviderLabel = (provider: AiProvider) => {
  switch (provider) {
    case "local":
      return "本地";
    case "ollama":
      return "Ollama";
    case "codex":
      return "Codex";
    default:
      return "Hermes";
  }
};

export function AiAgentPanel({ onClose }: { onClose?: () => void } = {}) {
  const [input, setInput] = useState(DEFAULT_PROMPT);
  const [messages, setMessages] = useState<ChatMsg[]>([]);
  const [logs, setLogs] = useState<ToolLog[]>([]);
  const [running, setRunning] = useState(false);
  const [showLogs, setShowLogs] = useState(true);
  const [aiProvider, setAiProvider] = useState<AiProvider>(DEFAULT_PREFS.provider);
  const [aiModel, setAiModel] = useState(DEFAULT_PREFS.model);
  const [maxSteps, setMaxSteps] = useState(DEFAULT_PREFS.maxSteps);

  const abortRef = useRef<AbortController | null>(null);
  const messageEndRef = useRef<HTMLDivElement | null>(null);

  const canSend = useMemo(() => input.trim().length > 0 && !running, [input, running]);
  const canClear = messages.length > 0 || logs.length > 0;
  const assistantCount = messages.filter((message) => message.role === "assistant").length;
  const userCount = messages.length - assistantCount;

  useEffect(() => {
    const prefs = readAiPanelPrefs("global_ai", DEFAULT_PREFS);
    setAiProvider(prefs.provider);
    setAiModel(prefs.model);
    setMaxSteps(clampStep(prefs.maxSteps));
  }, []);

  useEffect(() => {
    writeAiPanelPrefs("global_ai", { provider: aiProvider, model: aiModel, maxSteps: clampStep(maxSteps) });
  }, [aiModel, aiProvider, maxSteps]);

  useEffect(() => {
    messageEndRef.current?.scrollIntoView({ block: "end" });
  }, [messages, logs, showLogs]);

  const stop = () => {
    abortRef.current?.abort();
    abortRef.current = null;
    setRunning(false);
  };

  const restoreDefaultPrompt = () => setInput(DEFAULT_PROMPT);

  const clearConversation = () => {
    if (running) stop();
    setMessages([]);
    setLogs([]);
  };

  const send = async () => {
    const text = input.trim();
    if (!text) return;

    const nextMessages: ChatMsg[] = [...messages, { role: "user", content: text }];
    setMessages(nextMessages);
    setInput("");
    setRunning(true);
    setLogs([]);

    const controller = new AbortController();
    abortRef.current = controller;

    try {
      const res = await fetch("/api/ai-agent/run", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        signal: controller.signal,
        body: JSON.stringify({
          stream: true,
          maxSteps,
          scope: "global",
          messages: nextMessages.slice(-20),
          toolChoice: {
            mode: "auto",
            toolSets: TOOLSET_CHIPS.map((chip) => chip.id),
          },
          options: {
            searxng: true,
            ai: {
              provider: aiProvider,
              ...(aiProvider !== "codex" && aiModel.trim() ? { model: aiModel.trim() } : {}),
            },
          },
        }),
      });
      if (!res.ok) {
        const j = (await res.json().catch(() => null)) as unknown;
        const err = typeof j === "object" && j && "error" in j ? String((j as Record<string, unknown>).error ?? "") : "";
        throw new Error(err || `HTTP ${res.status}`);
      }

      await parseSseChunks(res, (event, dataText) => {
        if (event === "assistant_message") {
          try {
            const data = JSON.parse(dataText || "null") as unknown;
            const assistantText =
              typeof data === "object" && data && "text" in data ? String((data as Record<string, unknown>).text ?? "") : "";
            if (assistantText) setMessages((prev) => [...prev, { role: "assistant", content: assistantText }]);
          } catch {
            // ignore
          }
          return;
        }
        if (event === "tool_call") {
          try {
            const data = JSON.parse(dataText || "null") as unknown;
            const obj = (typeof data === "object" && data ? (data as Record<string, unknown>) : {}) as Record<string, unknown>;
            setLogs((prev) => [
              ...prev,
              {
                type: "tool_call",
                id: String(obj.id ?? ""),
                tool: String(obj.tool ?? ""),
                args: (typeof obj.args === "object" && obj.args ? (obj.args as Record<string, unknown>) : {}) as Record<string, unknown>,
              },
            ]);
          } catch {
            // ignore
          }
          return;
        }
        if (event === "tool_result") {
          try {
            const data = JSON.parse(dataText || "null") as unknown;
            const obj = (typeof data === "object" && data ? (data as Record<string, unknown>) : {}) as Record<string, unknown>;
            setLogs((prev) => [
              ...prev,
              {
                type: "tool_result",
                id: String(obj.id ?? ""),
                tool: String(obj.tool ?? ""),
                ok: Boolean(obj.ok),
                ms: Number(obj.ms ?? 0),
                result: "result" in obj ? obj.result : null,
              },
            ]);
          } catch {
            // ignore
          }
          return;
        }
        if (event === "error") {
          try {
            const data = JSON.parse(dataText || "null") as unknown;
            const message =
              typeof data === "object" && data && "message" in data ? String((data as Record<string, unknown>).message ?? "") : "";
            setLogs((prev) => [...prev, { type: "error", message: message || "未知错误" }]);
          } catch {
            setLogs((prev) => [...prev, { type: "error", message: "未知错误" }]);
          }
        }
      });
    } catch (error) {
      if (controller.signal.aborted || isAbortLikeError(error)) return;
      const message = error instanceof Error ? error.message : String(error);
      setLogs((prev) => [...prev, { type: "error", message }]);
    } finally {
      abortRef.current = null;
      setRunning(false);
    }
  };

  const logSidebar = showLogs ? (
    <div className="flex min-h-0 flex-1 flex-col">
      <div className="border-b border-white/8 px-4 py-4">
        <div className="text-sm font-medium text-white">活动轨迹</div>
        <div className="mt-1 text-xs text-white/45">前端已经退为桥接层，这里只显示回流事件。</div>
      </div>
      <div className="flex min-h-0 flex-1 flex-col gap-3 px-4 py-4">
        {logs.length === 0 ? <div className="text-sm text-white/45">暂无工具活动</div> : null}
        {logs.map((log, index) => (
          <article key={`${log.type}-${index}`} className={`rounded-2xl border p-3 ${getLogToneClass(log)}`}>
            <div className="flex items-center justify-between gap-3">
              <div className="text-xs font-medium tracking-wide text-white/82">{getLogLabel(log)}</div>
              {log.type !== "error" ? <div className="text-[11px] text-white/40">{log.id}</div> : null}
            </div>
            <div className="mt-2 text-sm font-medium text-white">{log.type === "error" ? log.message : log.tool}</div>
            {log.type === "tool_result" ? (
              <div className="mt-1 text-xs text-white/45">{log.ok ? "成功" : "失败"} · {log.ms}ms</div>
            ) : null}
            {log.type === "tool_call" ? (
              <pre className="mt-3 whitespace-pre-wrap text-xs leading-6 text-white/70">{formatJson(log.args)}</pre>
            ) : null}
            {log.type === "tool_result" ? (
              <pre className="mt-3 whitespace-pre-wrap text-xs leading-6 text-white/70">{formatJson(log.result)}</pre>
            ) : null}
          </article>
        ))}
      </div>
    </div>
  ) : undefined;

  return (
    <AiBridgePanel
      title="全局 AI"
      subtitle="实验入口"
      status={`${running ? "运行中" : "待命"} · ${getProviderLabel(aiProvider)}`}
      onClose={onClose}
      secondaryActions={
        <>
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            aria-label="切换工具活动面板"
            title={showLogs ? "隐藏工具活动面板" : "显示工具活动面板"}
            className="rounded-xl border border-white/10 bg-white/[0.03] text-white/80 hover:bg-white/[0.08] hover:text-white"
            onClick={() => setShowLogs((prev) => !prev)}
          >
            {showLogs ? <PanelRightClose className="h-4 w-4" /> : <PanelRightOpen className="h-4 w-4" />}
          </Button>
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            aria-label="恢复示例问题"
            title="恢复示例问题"
            className="rounded-xl border border-white/10 bg-white/[0.03] text-white/80 hover:bg-white/[0.08] hover:text-white"
            onClick={restoreDefaultPrompt}
          >
            <RefreshCcw className="h-4 w-4" />
          </Button>
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            aria-label="清空对话"
            title="清空对话"
            disabled={!canClear}
            className="rounded-xl border border-white/10 bg-white/[0.03] text-white/80 hover:bg-white/[0.08] hover:text-white"
            onClick={clearConversation}
          >
            <Trash2 className="h-4 w-4" />
          </Button>
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            aria-label="停止运行"
            title="停止本轮运行"
            disabled={!running}
            className="rounded-xl border border-white/10 bg-white/[0.03] text-white/80 hover:bg-white/[0.08] hover:text-white"
            onClick={stop}
          >
            <SquareStop className="h-4 w-4" />
          </Button>
        </>
      }
      sidebar={logSidebar}
      scrollBody={false}
      className="h-[calc(100vh-24px)] min-h-0 rounded-[24px] border-white/10 shadow-none"
    >
      <div className="flex min-h-0 flex-1 flex-col">
        <div className="border-b border-white/8 px-5 py-4">
          <div className="flex flex-wrap items-center justify-between gap-3">
            <div>
              <div className="text-xs uppercase tracking-[0.22em] text-white/45">当前能力</div>
              <div className="mt-2 text-sm text-white/65">保留 MNOTE 当前真实工具，不再在前端继续扩张编排层。</div>
            </div>
            <div className="flex flex-wrap items-center gap-2">
              <Badge className="border-white/10 bg-white/[0.04] text-white/70 hover:bg-white/[0.04]" variant="outline">
                用户 {userCount}
              </Badge>
              <Badge className="border-white/10 bg-white/[0.04] text-white/70 hover:bg-white/[0.04]" variant="outline">
                AI {assistantCount}
              </Badge>
              <Badge className="border-white/10 bg-white/[0.04] text-white/70 hover:bg-white/[0.04]" variant="outline">
                活动 {logs.length}
              </Badge>
            </div>
          </div>

          <div className="mt-4 flex flex-wrap gap-2">
            {TOOLSET_CHIPS.map((chip) => (
              <Badge
                key={chip.id}
                variant="outline"
                className="rounded-full border-white/10 bg-white/[0.03] px-3 py-1.5 text-white/78 hover:bg-white/[0.03]"
                title={chip.description}
              >
                {chip.title}
              </Badge>
            ))}
          </div>
        </div>

        <div className="min-h-0 flex-1">
          <ScrollArea className="h-full">
            <div className="mx-auto flex max-w-4xl flex-col gap-4 px-5 py-5">
              {messages.length === 0 ? (
                <div className="max-w-3xl rounded-[24px] border border-white/10 bg-white/[0.04] p-5 shadow-[0_10px_40px_rgba(0,0,0,0.24)]">
                  <div className="text-base font-semibold text-white">你好，我是 MNOTE 全局 AI。</div>
                  <div className="mt-2 text-sm leading-7 text-white/72">我会基于当前已接入的工具完成可追溯的检索、阅读与受控写入任务：</div>
                  <div className="mt-4 grid gap-3 sm:grid-cols-2">
                    {CAPABILITY_ITEMS.map((item) => {
                      const Icon = item.icon;
                      return (
                        <div key={item.title} className="rounded-2xl border border-white/8 bg-black/15 p-3">
                          <div className="flex items-center gap-2 text-sm font-medium text-white">
                            <Icon className="h-4 w-4 text-sky-200/80" />
                            {item.title}
                          </div>
                          <div className="mt-2 text-xs leading-6 text-white/58">{item.description}</div>
                        </div>
                      );
                    })}
                  </div>
                  <div className="mt-4 text-sm text-white/70">请输入你的目标，我会在允许的工具范围内完成分析，并把活动轨迹放到右侧。</div>
                </div>
              ) : null}

              {messages.map((message, index) => {
                const isUser = message.role === "user";
                return (
                  <article key={`${message.role}-${index}`} className={`flex gap-3 ${isUser ? "justify-end" : "justify-start"}`}>
                    <div
                      className={`max-w-[min(760px,92%)] rounded-[22px] border px-4 py-3 text-sm leading-7 shadow-[0_10px_30px_rgba(0,0,0,0.16)] ${
                        isUser ? "border-sky-400/25 bg-sky-500/15 text-sky-50" : "border-white/10 bg-white/[0.04] text-white/90"
                      }`}
                    >
                      <div className={`mb-2 text-xs ${isUser ? "text-sky-100/70" : "text-white/45"}`}>{isUser ? "我" : "AI"}</div>
                      <div className="whitespace-pre-wrap break-words">{message.content}</div>
                    </div>
                  </article>
                );
              })}
              <div ref={messageEndRef} />
            </div>
          </ScrollArea>
        </div>

        <div className="border-t border-white/8 px-5 py-4">
          <div className="mb-3 flex flex-wrap items-center justify-between gap-3">
            <div className="flex flex-wrap gap-2">
              {TOOLSET_CHIPS.map((chip) => (
                <div key={`${chip.id}-summary`} className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5">
                  <span className="text-xs font-medium text-white/82">{chip.title}</span>
                  <span className="text-xs text-white/42">{chip.description}</span>
                </div>
              ))}
            </div>

            <div className="flex flex-wrap items-center gap-3">
              <label className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5 text-xs text-white/68">
                最大步数
                <input
                  className="w-14 rounded-md border border-white/10 bg-black/20 px-2 py-1 text-right text-white outline-none"
                  type="number"
                  min={MIN_PANEL_AGENT_STEPS}
                  max={MAX_PANEL_AGENT_STEPS}
                  step={1}
                  value={maxSteps}
                  onChange={(e) => {
                    const value = Number(e.target.value);
                    if (!Number.isFinite(value)) return;
                    setMaxSteps(clampStep(Math.floor(value)));
                  }}
                  disabled={running}
                />
              </label>

              <select
                className="h-9 rounded-full border border-white/10 bg-white/[0.03] px-3 text-xs text-white outline-none"
                value={aiProvider}
                onChange={(e) => {
                  const value = String(e.target.value || "").trim();
                  if (value === "online" || value === "local" || value === "ollama" || value === "codex") {
                    setAiProvider(value);
                    return;
                  }
                  setAiProvider("online");
                }}
                disabled={running}
              >
                <option value="online">Hermes</option>
                <option value="local">本地</option>
                <option value="ollama">Ollama</option>
                <option value="codex">Codex</option>
              </select>

              <input
                className="h-9 w-44 rounded-full border border-white/10 bg-white/[0.03] px-3 text-xs text-white outline-none placeholder:text-white/30"
                value={aiModel}
                onChange={(e) => setAiModel(e.target.value)}
                placeholder={aiProvider === "codex" ? "Codex 无需模型" : "模型（可选）"}
                disabled={running || aiProvider === "codex"}
              />
            </div>
          </div>

          <div className="relative">
            <Textarea
              value={input}
              onChange={(e) => setInput(e.target.value)}
              placeholder="输入问题，Enter 发送，Shift+Enter 换行"
              className="min-h-[140px] rounded-[24px] border-white/10 bg-white/[0.03] px-4 py-4 pb-16 pr-40 text-sm leading-7 text-white placeholder:text-white/30"
              onKeyDown={(e) => {
                if (e.key === "Enter" && !e.shiftKey) {
                  e.preventDefault();
                  if (canSend) void send();
                }
              }}
            />

            <div className="pointer-events-none absolute bottom-4 left-4 text-xs text-white/38">Enter 发送，Shift+Enter 换行</div>

            {running ? (
              <Button
                type="button"
                variant="secondary"
                className="absolute bottom-4 right-16 rounded-xl border border-white/10 bg-white/[0.06] text-white hover:bg-white/[0.12]"
                onClick={stop}
              >
                停止
              </Button>
            ) : null}

            <Button
              type="button"
              disabled={!canSend}
              aria-label="发送消息"
              title="发送"
              className="absolute bottom-4 right-4 h-10 w-10 rounded-full bg-sky-500 p-0 text-white hover:bg-sky-400"
              onClick={() => void send()}
            >
              <SendHorizontal className="h-4 w-4" />
            </Button>
          </div>
        </div>
      </div>
    </AiBridgePanel>
  );
}
