"use client";

import React, { useEffect, useMemo, useRef, useState } from "react";
import { Network, Paperclip, Settings2, Send, X } from "lucide-react";

type AgentAssetItem = {
  kind: "media" | "local-mindmap" | "test-pdf";
  id: string;
  title: string;
  fileUrl: string;
  mimeType?: string | null;
  assetType?: string | null;
  fileName?: string | null;
};

type AgentMessage = { role: "user" | "assistant"; content: string };

type ToolName =
  | "mindmap_get"
  | "mindmap_get_subtree"
  | "search_web"
  | "mindmap_apply_ops"
  | "pdf_replace_mindmap";

const TOOL_LABEL: Record<ToolName, string> = {
  mindmap_get: "读导图（摘要）",
  mindmap_get_subtree: "读子树（按 uid）",
  search_web: "联网检索（SearxNG）",
  mindmap_apply_ops: "写入导图（ops）",
  pdf_replace_mindmap: "PDF→导图（替换当前）",
};

const DEFAULT_TOOLS: ToolName[] = [
  "mindmap_get",
  "mindmap_get_subtree",
  "search_web",
  "mindmap_apply_ops",
  "pdf_replace_mindmap",
];

const ONLINE_MODELS = [
  "",
  "gemini-2.5-flash",
  "gemini-2.5-pro",
  "gemini-3-pro-preview",
  "gemini-3-flash-preview",
] as const;

export function MindmapAiAgentPanel({
  documentId,
  mindmapId,
  mindmap,
  activeNodes,
}: {
  documentId: string;
  mindmapId: string;
  mindmap: any;
  activeNodes: any[];
}) {
  const [messages, setMessages] = useState<AgentMessage[]>([
    {
      role: "assistant",
      content:
        "你好，我是思维导图 AI Agent。你可以：\n- 直接说需求（例如：补完选中节点、总结 @PDF 并写入导图、从 @PDF 生成章/节结构导图）\n- 使用 @ 选择文件或上传文件\n- 让 AI 自动选择工具，或手动勾选允许使用的工具",
    },
  ]);
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);

  const [networkOn, setNetworkOn] = useState(true);
  const [toolAuto, setToolAuto] = useState(true);
  const [toolPickerOpen, setToolPickerOpen] = useState(false);
  const [selectedTools, setSelectedTools] = useState<ToolName[]>(DEFAULT_TOOLS);
  const [aiProvider, setAiProvider] = useState<"online" | "local">("online");
  const [aiModel, setAiModel] = useState<string>("");

  const [assets, setAssets] = useState<AgentAssetItem[]>([]);
  const [workspaceId, setWorkspaceId] = useState<string>("");
  const [attachments, setAttachments] = useState<AgentAssetItem[]>([]);
  const [debug, setDebug] = useState<string>("");

  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
  const fileInputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    try {
      const p = (window.localStorage.getItem("mindmap_ai_provider") || "").trim();
      const m = window.localStorage.getItem("mindmap_ai_model") || "";
      if (p === "local" || p === "online") setAiProvider(p);
      if (typeof m === "string") setAiModel(m);
    } catch {
      // ignore
    }
  }, []);

  useEffect(() => {
    try {
      window.localStorage.setItem("mindmap_ai_provider", aiProvider);
      window.localStorage.setItem("mindmap_ai_model", aiModel);
    } catch {
      // ignore
    }
  }, [aiProvider, aiModel]);

  // @ 选择
  const [mentionOpen, setMentionOpen] = useState(false);
  const [mentionQuery, setMentionQuery] = useState("");
  const [mentionRange, setMentionRange] = useState<{ start: number; end: number } | null>(null);

  const persistMindmapData = (data: unknown): boolean => {
    try {
      const w = window as unknown as {
        __mindmapPersistById?: Record<string, (data: unknown) => void>;
      };
      const fn = w.__mindmapPersistById?.[mindmapId];
      if (typeof fn === "function") {
        fn(data);
        return true;
      }
    } catch {
      // ignore
    }
    return false;
  };

  const selectedUids = useMemo(() => {
    const list = Array.isArray(activeNodes) ? activeNodes : [];
    return list
      .slice(0, 3)
      .map((n) => String(n?.nodeData?.data?.uid ?? n?.nodeData?.uid ?? n?.getData?.("uid") ?? n?.uid ?? ""))
      .filter(Boolean);
  }, [activeNodes]);

  useEffect(() => {
    let cancelled = false;
    if (!documentId) return;
    void (async () => {
      try {
        const res = await fetch(`/api/mindmap-ai/assets?documentId=${encodeURIComponent(documentId)}`);
        const json = (await res.json().catch(() => null)) as any;
        if (!res.ok) return;
        if (cancelled) return;
        setWorkspaceId(String(json?.workspaceId ?? ""));
        setAssets(Array.isArray(json?.items) ? (json.items as AgentAssetItem[]) : []);
      } catch {
        // ignore
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [documentId]);

  const filteredAssets = useMemo(() => {
    const q = mentionQuery.trim().toLowerCase();
    const base = assets.slice(0, 200);
    if (!q) return base.slice(0, 12);
    return base
      .filter((a) => {
        const hay = `${a.title} ${a.fileName ?? ""}`.toLowerCase();
        return hay.includes(q);
      })
      .slice(0, 12);
  }, [assets, mentionQuery]);

  const updateMentionState = (value: string, cursor: number) => {
    const before = value.slice(0, Math.max(0, cursor));
    const at = before.lastIndexOf("@");
    if (at === -1) {
      setMentionOpen(false);
      setMentionQuery("");
      setMentionRange(null);
      return;
    }
    // 若 @ 前是非空白字符，视为 email/路径等，避免误触发
    if (at > 0 && /\S/.test(before[at - 1] || "")) {
      setMentionOpen(false);
      setMentionQuery("");
      setMentionRange(null);
      return;
    }
    const token = before.slice(at + 1);
    // 遇到换行/空格则不触发
    if (/\s/.test(token)) {
      setMentionOpen(false);
      setMentionQuery("");
      setMentionRange(null);
      return;
    }
    setMentionOpen(true);
    setMentionQuery(token);
    setMentionRange({ start: at, end: cursor });
  };

  const insertMention = (item: AgentAssetItem) => {
    const el = textareaRef.current;
    if (!el || !mentionRange) return;
    const next = `${input.slice(0, mentionRange.start)}@${item.title}${input.slice(mentionRange.end)}`;
    setInput(next);
    setMentionOpen(false);
    setMentionQuery("");
    setMentionRange(null);
    // 去重加入附件
    setAttachments((prev) => (prev.some((x) => x.id === item.id) ? prev : [...prev, item]));
    // 光标移动到插入后
    window.setTimeout(() => {
      try {
        const pos = mentionRange.start + 1 + item.title.length;
        el.focus();
        el.setSelectionRange(pos, pos);
      } catch {
        // ignore
      }
    }, 0);
  };

  const toggleTool = (tool: ToolName) => {
    setSelectedTools((prev) => {
      if (prev.includes(tool)) return prev.filter((t) => t !== tool);
      return [...prev, tool];
    });
  };

  const uploadFiles = async (files: FileList | null) => {
    if (!files || files.length === 0) return;
    if (!workspaceId || !documentId) {
      window.alert("缺少 workspaceId/documentId，无法上传。请刷新后重试。");
      return;
    }
    const file = files[0];
    const form = new FormData();
    form.append("file", file);
    form.append("workspaceId", workspaceId);
    form.append("documentId", documentId);

    setLoading(true);
    try {
      const res = await fetch("/api/media/upload", { method: "POST", body: form });
      const json = (await res.json().catch(() => null)) as any;
      if (!res.ok) throw new Error(String(json?.error ?? `上传失败：${res.status}`));
      const asset = json?.asset;
      const item: AgentAssetItem = {
        kind: "media",
        id: String(asset?.id ?? `media:${Date.now()}`),
        title: String(asset?.file_name ?? file.name),
        fileUrl: String(asset?.file_url ?? ""),
        mimeType: String(asset?.mime_type ?? file.type ?? ""),
        assetType: String(asset?.asset_type ?? "file"),
        fileName: String(asset?.file_name ?? file.name),
      };
      setAttachments((prev) => (prev.some((x) => x.id === item.id) ? prev : [...prev, item]));
      // 刷新资产列表
      try {
        const listRes = await fetch(`/api/mindmap-ai/assets?documentId=${encodeURIComponent(documentId)}`);
        const listJson = (await listRes.json().catch(() => null)) as any;
        if (listRes.ok && Array.isArray(listJson?.items)) {
          setAssets(listJson.items as AgentAssetItem[]);
          setWorkspaceId(String(listJson?.workspaceId ?? workspaceId));
        }
      } catch {
        // ignore
      }
    } catch (e) {
      window.alert(e instanceof Error ? e.message : String(e));
    } finally {
      setLoading(false);
      if (fileInputRef.current) fileInputRef.current.value = "";
    }
  };

  const send = async () => {
    const content = input.trim();
    if (!content) return;
    setDebug("");

    const nextMessages: AgentMessage[] = [...messages, { role: "user", content }];
    setMessages(nextMessages);
    setInput("");
    setLoading(true);

    try {
      const res = await fetch("/api/mindmap-ai/agent", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          documentId,
          mindmapId,
          selectedUids,
          messages: nextMessages,
          attachments: attachments.map((a) => ({
            id: a.id,
            title: a.title,
            fileUrl: a.fileUrl,
            mimeType: a.mimeType ?? null,
          })),
          toolChoice: toolAuto ? { mode: "auto" } : { mode: "manual", tools: selectedTools },
          options: { searxng: networkOn, ai: { provider: aiProvider, model: aiModel.trim() || undefined } },
        }),
      });
      const json = (await res.json().catch(() => null)) as any;
      if (!res.ok) throw new Error(String(json?.error ?? `请求失败：${res.status}`));

      const assistantText = String(json?.message ?? "");
      setMessages((prev) => [...prev, { role: "assistant", content: assistantText || "（无输出）" }]);

      if (json?.data) {
        mindmap?.setData?.(json.data);
        mindmap?.command?.clearHistory?.();
        persistMindmapData(json.data);
      }

      if (json?.trace) {
        setDebug(JSON.stringify(json.trace, null, 2));
      }
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      setMessages((prev) => [...prev, { role: "assistant", content: `执行失败：${msg}` }]);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="flex h-full flex-col">
      <div className="flex items-center justify-between gap-2 pb-3">
        <div className="text-xs text-gray-500">
          {selectedUids.length ? `选中节点：${selectedUids[0]}` : "未选中节点（将以整图为上下文）"}
        </div>
        <div className="flex items-center gap-1">
          <button
            type="button"
            className={`inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs ${networkOn ? "border-blue-200 bg-blue-50 text-blue-700" : "border-gray-200 text-gray-500"}`}
            title="联网检索（SearxNG）"
            onClick={() => setNetworkOn((v) => !v)}
          >
            <Network className="h-3 w-3" />
            联网
          </button>
          <button
            type="button"
            className={`inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs ${toolPickerOpen ? "border-gray-300 bg-gray-50 text-gray-700" : "border-gray-200 text-gray-600"}`}
            title="选择允许使用的工具"
            onClick={() => setToolPickerOpen((v) => !v)}
          >
            <Settings2 className="h-3 w-3" />
            工具
          </button>
        </div>
      </div>

      {toolPickerOpen && (
        <div className="mb-3 rounded-md border border-gray-200 bg-white p-2">
          <div className="mb-2 flex items-center justify-between">
            <div className="text-xs font-medium text-gray-700">工具选择</div>
            <button
              type="button"
              className={`rounded px-2 py-1 text-xs ${toolAuto ? "bg-blue-500 text-white" : "border border-gray-200 text-gray-600"}`}
              onClick={() => setToolAuto((v) => !v)}
              title="自动：AI 自行选择；手动：仅允许勾选工具"
            >
              {toolAuto ? "自动" : "手动"}
            </button>
          </div>
          <div className="space-y-1">
            {DEFAULT_TOOLS.map((t) => (
              <label key={t} className={`flex cursor-pointer items-center gap-2 text-xs ${toolAuto ? "opacity-50" : ""}`}>
                <input
                  type="checkbox"
                  disabled={toolAuto}
                  checked={selectedTools.includes(t)}
                  onChange={() => toggleTool(t)}
                />
                <span className="text-gray-700">{TOOL_LABEL[t]}</span>
              </label>
            ))}
          </div>
          <div className="mt-2 text-[11px] text-gray-400">
            提示：如果你希望 AI 一定要“写入导图”，请在需求中明确说“请写入并保存”。
          </div>
        </div>
      )}

      <div className="flex-1 overflow-y-auto rounded-md border border-gray-200 bg-white p-2">
        <div className="space-y-2">
          {messages.map((m, idx) => (
            <div
              key={idx}
              className={`whitespace-pre-wrap rounded-md px-2 py-2 text-sm ${m.role === "user" ? "bg-gray-50 text-gray-900" : "bg-white text-gray-800"}`}
            >
              <div className="mb-1 text-[11px] text-gray-400">{m.role === "user" ? "你" : "AI"}</div>
              <div>{m.content}</div>
            </div>
          ))}
        </div>
      </div>

      {attachments.length > 0 && (
        <div className="mt-2 flex flex-wrap gap-1">
          {attachments.map((a) => (
            <span
              key={a.id}
              className="inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-50 px-2 py-1 text-[11px] text-gray-700"
              title={a.fileUrl}
            >
              @{a.title}
              <button
                type="button"
                className="text-gray-400 hover:text-gray-700"
                onClick={() => setAttachments((prev) => prev.filter((x) => x.id !== a.id))}
              >
                <X className="h-3 w-3" />
              </button>
            </span>
          ))}
        </div>
      )}

      <div className="mt-2 rounded-md border border-gray-200 bg-white px-2 py-2 text-xs text-gray-700">
        <div className="flex flex-wrap items-center gap-3">
          <div className="text-gray-500">AI：</div>
          <label className="inline-flex cursor-pointer items-center gap-1">
            <input
              type="radio"
              name="mindmap-ai-provider"
              data-testid="mindmap-ai-provider-online"
              checked={aiProvider === "online"}
              onChange={() => setAiProvider("online")}
            />
            在线
          </label>
          <label className="inline-flex cursor-pointer items-center gap-1">
            <input
              type="radio"
              name="mindmap-ai-provider"
              data-testid="mindmap-ai-provider-local"
              checked={aiProvider === "local"}
              onChange={() => setAiProvider("local")}
            />
            本地
          </label>
          <div className="flex items-center gap-2">
            <div className="text-gray-500">模型：</div>
            {aiProvider === "online" ? (
              <select
                data-testid="mindmap-ai-model-select"
                className="rounded border border-gray-200 bg-white px-2 py-1 text-xs outline-none focus:border-blue-300"
                value={aiModel}
                onChange={(e) => setAiModel(e.target.value)}
              >
                {ONLINE_MODELS.map((m) => (
                  <option key={m || "__default__"} value={m}>
                    {m ? m : "默认（ai.md）"}
                  </option>
                ))}
              </select>
            ) : (
              <input
                data-testid="mindmap-ai-model-input"
                className="w-[220px] rounded border border-gray-200 bg-white px-2 py-1 text-xs outline-none focus:border-blue-300"
                value={aiModel}
                onChange={(e) => setAiModel(e.target.value)}
                placeholder="默认（ai.local.md/环境变量）"
              />
            )}
          </div>
        </div>
        {aiProvider === "local" ? (
          <div className="mt-1 text-[11px] text-gray-400">
            本地 AI 需配置 `LOCAL_AI_BASE_URL`/`LOCAL_AI_MODEL` 或 `ai.local.md` / `ai-local.md`
          </div>
        ) : null}
      </div>

      <div className="relative mt-2">
        {mentionOpen && filteredAssets.length > 0 && (
          <div className="absolute bottom-[calc(100%+8px)] left-0 right-0 z-30 max-h-56 overflow-auto rounded-md border border-gray-200 bg-white shadow">
            {filteredAssets.map((a) => (
              <button
                key={a.id}
                type="button"
                className="flex w-full items-center justify-between gap-2 px-3 py-2 text-left text-sm hover:bg-gray-50"
                onClick={() => insertMention(a)}
              >
                <span className="truncate text-gray-800">{a.title}</span>
                <span className="shrink-0 text-[11px] text-gray-400">{a.kind}</span>
              </button>
            ))}
          </div>
        )}

        <textarea
          ref={textareaRef}
          data-testid="mindmap-ai-input"
          className="w-full resize-none rounded-md border border-gray-200 bg-white p-2 text-sm outline-none focus:border-blue-300"
          rows={4}
          value={input}
          placeholder="输入你的需求。使用 @ 选择文件（PDF/附件/本地导图），例如：总结 @卤化反应原理_1-9.pdf 并写入导图（章->节->要点）。"
          onChange={(e) => {
            const v = e.target.value;
            setInput(v);
            updateMentionState(v, e.target.selectionStart ?? v.length);
          }}
          onKeyDown={(e) => {
            // 焦点在 AI 输入框时，不应触发导图 Enter/Tab 快捷键
            e.stopPropagation();
            if (e.key === "Escape") {
              setMentionOpen(false);
              setToolPickerOpen(false);
              return;
            }
            if (e.key === "Enter") {
              // Enter 发送；Shift+Enter 换行
              if (!e.shiftKey && !e.isComposing) {
                e.preventDefault();
                if (!loading) void send();
              }
              return;
            }
          }}
          onClick={(e) => {
            const el = e.currentTarget;
            updateMentionState(el.value, el.selectionStart ?? el.value.length);
          }}
        />

        <div className="mt-2 flex items-center justify-between gap-2">
          <div className="flex items-center gap-2">
            <button
              type="button"
              className="inline-flex items-center gap-1 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-600 hover:bg-gray-50"
              onClick={() => fileInputRef.current?.click()}
              title="上传文件到当前页面附件"
              disabled={loading}
            >
              <Paperclip className="h-3 w-3" />
              上传
            </button>
            <input
              ref={fileInputRef}
              type="file"
              className="hidden"
              onChange={(e) => {
                void uploadFiles(e.target.files);
              }}
              accept="*/*"
            />
            <div className="text-[11px] text-gray-400">Enter 发送 · Shift+Enter 换行</div>
          </div>

          <button
            type="button"
            className="inline-flex items-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm text-white hover:bg-blue-700 disabled:opacity-50"
            disabled={loading || !input.trim()}
            onClick={() => void send()}
          >
            <Send className="h-4 w-4" />
            {loading ? "执行中..." : "发送"}
          </button>
        </div>
      </div>

      {debug ? (
        <details className="mt-2 rounded-md border border-gray-200 bg-white p-2 text-xs text-gray-600">
          <summary className="cursor-pointer select-none">调试信息（tool trace）</summary>
          <pre className="mt-2 max-h-56 overflow-auto whitespace-pre-wrap">{debug}</pre>
        </details>
      ) : null}
    </div>
  );
}
