import { useCallback, useEffect, useMemo, useState } from "react";
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  addEdge,
  useNodesState,
  useEdgesState,
  type Connection,
  type Node,
  type Edge,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";

import { Play, ShieldCheck, Sparkles, Trash2, Save, Workflow } from "lucide-react";
import { nodeTypes, type NodeStatus } from "./TypedNode";
import { Inspector } from "./Inspector";
import { NODE_KINDS, NODE_ORDER } from "./nodeCatalog";
import { exportDsl, validate, type Issue, type TaskDsl } from "../lib/dsl";
import type { RunPhase } from "../lib/run";
import { listKb, listChatModels, listAgents, saveAgent, deleteAgent, type Identity, type AgentInfo } from "../lib/api";
import { useTheme } from "../lib/theme";
import { Button, Input, cn, useToast } from "../ui";

let seq = 0;

// buildExample：一张可直接运行的示例图（输入 → 检索本人 default 库 → Agent → 输出）。
function buildExample(): { nodes: Node[]; edges: Edge[] } {
  const mk = (id: string, kind: string, x: number, cfg: Record<string, unknown>): Node => ({
    id,
    type: "typed",
    position: { x, y: 140 },
    data: { kind, label: NODE_KINDS[kind].label, config: { ...NODE_KINDS[kind].defaults, ...cfg }, status: "idle" as NodeStatus },
  });
  return {
    nodes: [
      mk("ex-in", "input", 40, { text: "sundynix-agentix 的架构是怎样的？" }),
      mk("ex-rag", "retriever", 300, { kb: "default", topK: 4 }),
      mk("ex-agent", "agent", 560, { system: "你是知识库问答助手，依据检索到的资料严谨作答。" }),
      mk("ex-out", "output", 820, {}),
    ],
    edges: [
      { id: "ex-e1", source: "ex-in", target: "ex-rag" },
      { id: "ex-e2", source: "ex-rag", target: "ex-agent" },
      { id: "ex-e3", source: "ex-agent", target: "ex-out" },
    ],
  };
}

// 编排 Studio：左(节点面板 + 我的编排) · 中画布 · 右检查器 · 顶工具栏。
export function StudioView({ onRun, phase, identity, readOnly = false, spaceId = "", spaceReadOnly = false }: { onRun: (dsl: TaskDsl) => void; phase: RunPhase; identity: Identity; readOnly?: boolean; spaceId?: string; spaceReadOnly?: boolean }) {
  const toast = useToast();
  const { theme } = useTheme();
  const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
  const [selId, setSelId] = useState<string | null>(null);
  const [issues, setIssues] = useState<Issue[] | null>(null);
  const [kbOpts, setKbOpts] = useState<string[]>([]);
  const [modelOpts, setModelOpts] = useState<string[]>([]);
  const [agents, setAgents] = useState<AgentInfo[]>([]);
  const [name, setName] = useState("");

  useEffect(() => {
    listKb(identity).then((ks) => setKbOpts(ks.map((k) => k.name))).catch(() => {});
    listChatModels().then(setModelOpts).catch(() => {});
  }, [identity]);

  const refreshAgents = useCallback(() => {
    listAgents(identity).then(setAgents).catch(() => {});
  }, [identity]);
  // 切换工作区(spaceId 变)后重新拉取该空间的编排（共享工作区隔离）。
  useEffect(() => refreshAgents(), [refreshAgents, spaceId]);

  const dynamicOptions = useMemo(() => ({ kb: kbOpts, model: modelOpts }), [kbOpts, modelOpts]);

  const onConnect = useCallback((c: Connection) => setEdges((es) => addEdge(c, es)), [setEdges]);

  const addNode = useCallback(
    (kind: string) => {
      const id = `n${++seq}`;
      const k = NODE_KINDS[kind];
      setNodes((ns) => [
        ...ns,
        {
          id,
          type: "typed",
          position: { x: 120 + ((ns.length * 40) % 360), y: 60 + ns.length * 64 },
          data: { kind, label: k.label, config: { ...k.defaults }, status: "idle" as NodeStatus },
        },
      ]);
    },
    [setNodes],
  );

  const patchNode = useCallback(
    (id: string, patch: Record<string, unknown>) =>
      setNodes((ns) => ns.map((n) => (n.id === id ? { ...n, data: { ...n.data, ...patch } } : n))),
    [setNodes],
  );

  const deleteNode = useCallback(
    (id: string) => {
      setNodes((ns) => ns.filter((n) => n.id !== id));
      setEdges((es) => es.filter((e) => e.source !== id && e.target !== id));
      setSelId(null);
    },
    [setNodes, setEdges],
  );

  const loadGraph = useCallback(
    (g: { nodes?: Node[]; edges?: Edge[] }) => {
      // 防御：旧/损坏数据可能缺 position/type/data —— 全部兜底，避免 React Flow 崩成黑屏。
      const safeNodes: Node[] = (g.nodes ?? [])
        .filter((n) => n && n.id)
        .map((n, i) => {
          const d = (n.data ?? {}) as { kind?: string; label?: string; config?: Record<string, unknown> };
          const kind = d.kind && NODE_KINDS[d.kind] ? d.kind : "output";
          const pos = n.position && typeof n.position.x === "number" && typeof n.position.y === "number" ? n.position : { x: 100 + (i % 4) * 220, y: 80 + Math.floor(i / 4) * 120 };
          return {
            id: n.id,
            type: "typed",
            position: pos,
            data: { kind, label: d.label || NODE_KINDS[kind].label, config: d.config ?? {}, status: "idle" as NodeStatus },
          };
        });
      const ids = new Set(safeNodes.map((n) => n.id));
      const safeEdges: Edge[] = (g.edges ?? [])
        .filter((e) => e && e.source && e.target && ids.has(e.source) && ids.has(e.target))
        .map((e, i) => ({ ...e, id: e.id || `e${i}` }));
      setNodes(safeNodes);
      setEdges(safeEdges);
      setSelId(null);
      setIssues(null);
      for (const n of safeNodes) {
        const m = /^n(\d+)$/.exec(n.id);
        if (m) seq = Math.max(seq, Number(m[1]));
      }
    },
    [setNodes, setEdges],
  );

  const clear = () => {
    setNodes([]);
    setEdges([]);
    setSelId(null);
    setIssues(null);
  };

  const save = async () => {
    if (spaceReadOnly) return toast.push("error", "当前工作区你是只读成员（viewer），无权保存编排");
    const nm = name.trim();
    if (!nm) return toast.push("error", "先填编排名");
    if (nodes.length === 0) return toast.push("error", "画布为空");
    try {
      await saveAgent(identity, nm, JSON.stringify({ nodes, edges }));
      toast.push("success", `已保存编排「${nm}」`);
      refreshAgents();
    } catch (e) {
      toast.push("error", (e as Error).message);
    }
  };

  const openAgent = (a: AgentInfo) => {
    try {
      loadGraph(JSON.parse(a.graph));
      setName(a.name);
    } catch {
      toast.push("error", "编排数据损坏，无法载入");
    }
  };

  const removeAgent = async (nm: string) => {
    try {
      await deleteAgent(identity, nm);
      refreshAgents();
      toast.push("success", `已删除「${nm}」`);
    } catch (e) {
      toast.push("error", (e as Error).message);
    }
  };

  useEffect(() => {
    const status: NodeStatus =
      phase === "streaming" || phase === "submitting" ? "running" : phase === "done" ? "done" : phase === "error" ? "error" : "idle";
    setNodes((ns) => ns.map((n) => ({ ...n, data: { ...n.data, status } })));
  }, [phase, setNodes]);

  const run = useCallback(() => {
    if (readOnly) {
      toast.push("error", "当前身份为只读成员（viewer），无权在此租户运行编排");
      return;
    }
    const found = validate(nodes, edges);
    setIssues(found);
    if (!found.some((i) => i.level === "error")) onRun(exportDsl(nodes, edges));
  }, [nodes, edges, onRun, readOnly, toast]);

  const selected = useMemo(() => nodes.find((n) => n.id === selId) ?? null, [nodes, selId]);
  const running = phase === "submitting" || phase === "streaming";

  return (
    <div className="flex h-full">
      {/* 左：节点面板 + 我的编排 */}
      <div className="flex w-44 shrink-0 flex-col border-r border-line bg-ink-900">
        <div className="overflow-auto p-2">
          <div className="mb-1 px-1 text-[11px] font-semibold text-slate-500">节点</div>
          {NODE_ORDER.map((kind) => {
            const k = NODE_KINDS[kind];
            return (
              <button
                key={kind}
                onClick={() => addNode(kind)}
                className={`mb-1 flex w-full items-center gap-2 rounded-md border border-l-[3px] border-line bg-ink-800 px-2 py-1.5 text-left text-xs text-slate-300 hover:bg-ink-700 ${k.accent}`}
                title={k.desc}
              >
                {k.label}
              </button>
            );
          })}
        </div>
        <div className="flex min-h-0 flex-1 flex-col border-t border-line p-2">
          <div className="mb-1 flex items-center gap-1 px-1 text-[11px] font-semibold text-slate-500">
            <Workflow className="h-3.5 w-3.5" /> 空间编排 {agents.length > 0 && `(${agents.length})`}
          </div>
          <ul className="min-h-0 flex-1 space-y-0.5 overflow-auto">
            {agents.length === 0 && <li className="px-1 text-[11px] text-slate-600">保存后在此列出，同空间成员共享。</li>}
            {agents.map((a) => (
              <li key={a.name} className="group flex items-center gap-1 rounded hover:bg-ink-800">
                <button onClick={() => openAgent(a)} className="flex-1 truncate px-2 py-1.5 text-left text-xs text-slate-300" title={`载入「${a.name}」${a.creator ? ` · 由 ${a.creator} 创建` : ""}`}>
                  {a.name}
                  {a.creator && !a.mine && <span className="ml-1 text-[10px] text-slate-500">· {a.creator}</span>}
                </button>
                {(a.mine !== false && !spaceReadOnly) && (
                  <button onClick={() => removeAgent(a.name)} className="px-1 text-slate-600 opacity-0 hover:text-danger group-hover:opacity-100" title="删除">
                    <Trash2 className="h-3.5 w-3.5" />
                  </button>
                )}
              </li>
            ))}
          </ul>
        </div>
      </div>

      {/* 中画布 */}
      <div className="relative flex-1 bg-ink-950">
        <div className="absolute left-0 right-0 top-0 z-10 flex flex-wrap items-center gap-1.5 border-b border-line bg-ink-900/90 px-2 py-1.5 backdrop-blur">
          <Button variant="primary" size="sm" icon={Play} onClick={run} disabled={running || nodes.length === 0 || readOnly} title={readOnly ? "只读成员（viewer）无权在此租户运行编排" : undefined}>
            {running ? "运行中…" : readOnly ? "只读" : "运行"}
          </Button>
          <Button size="sm" icon={ShieldCheck} onClick={() => setIssues(validate(nodes, edges))}>
            校验
          </Button>
          <span className="mx-1 h-4 w-px bg-line" />
          <Button size="sm" icon={Sparkles} onClick={() => loadGraph(buildExample())}>
            示例
          </Button>
          <Button size="sm" icon={Trash2} onClick={clear} disabled={nodes.length === 0}>
            清空
          </Button>
          <span className="mx-1 h-4 w-px bg-line" />
          <Input className="h-8 w-32" value={name} onChange={(e) => setName(e.target.value)} placeholder="编排名" />
          <Button size="sm" variant="primary" icon={Save} onClick={save} disabled={nodes.length === 0 || spaceReadOnly} title={spaceReadOnly ? "当前工作区你是只读成员（viewer）" : undefined}>
            保存
          </Button>
          <span className="ml-auto text-[11px] text-slate-500">
            {nodes.length} 节点 · {edges.length} 连线
          </span>
          {issues && (
            <span className="text-[11px]">
              {issues.length === 0 ? <span className="text-success">✓ 校验通过</span> : <span className="text-warn">{issues.length} 项提示</span>}
            </span>
          )}
        </div>

        <ReactFlow
          colorMode={theme}
          proOptions={{ hideAttribution: true }}
          nodes={nodes}
          edges={edges}
          nodeTypes={nodeTypes}
          onNodesChange={onNodesChange}
          onEdgesChange={onEdgesChange}
          onConnect={onConnect}
          onNodeClick={(_, n) => setSelId(n.id)}
          onPaneClick={() => setSelId(null)}
          deleteKeyCode={["Backspace", "Delete"]}
          fitView
        >
          <Background color={theme === "dark" ? "#27272a" : "#e4e4e7"} gap={18} />
          <Controls />
          <MiniMap zoomable pannable maskColor={theme === "dark" ? "rgba(0,0,0,0.55)" : "rgba(0,0,0,0.08)"} className="!bg-ink-850" />
        </ReactFlow>

        {issues && issues.length > 0 && (
          <div className="absolute bottom-2 left-2 max-w-md rounded-lg border border-line bg-ink-850 p-2 text-[11px] shadow-card">
            {issues.map((i, idx) => (
              <div key={idx} className={cn(i.level === "error" ? "text-danger" : "text-warn")}>
                {i.level === "error" ? "✗" : "⚠"} {i.msg}
              </div>
            ))}
          </div>
        )}
      </div>

      {/* 右检查器 */}
      <div className="w-72 shrink-0 border-l border-line bg-ink-900">
        <Inspector node={selected} onChange={patchNode} onDelete={deleteNode} dynamicOptions={dynamicOptions} />
      </div>
    </div>
  );
}
