import type { StateCreator } from "zustand";
import { createId } from "@paralleldrive/cuid2";
import type {
  AnchorEdge,
  AnchorType,
  AnyLessonPlanEdge,
  Block,
  BlockType,
  FlowEdge,
  LessonPlanDocument,
  LessonPlanNode,
  NodeAnchor,
  TextbookContentNode,
  TextbookContentNodeData,
} from "../types";
import { defaultDataForType } from "../lib/document-migration";
import { computeAutoLayout } from "../lib/auto-layout";
import type { EditorState } from "./use-lesson-plan-editor";

export interface EditorSlice {
  planId: string;
  title: string;
  doc: LessonPlanDocument;
  setTitle: (title: string) => void;
  setPlanId: (planId: string) => void;
  addNode: (type: BlockType, position?: { x: number; y: number }, title?: string) => string;
  updateNode: (id: string, patch: Omit<Partial<Block>, "type">) => void;
  updateNodePosition: (id: string, position: { x: number; y: number }) => void;
  removeNode: (id: string) => void;
  updateTextbookContent: (data: Partial<TextbookContentNodeData>) => void;
  getTextbookContentNode: () => TextbookContentNode | undefined;
  addAnchor: (params: {
    nodeId: string;
    type: AnchorType;
    start: number;
    end?: number;
    textPreview?: string;
  }) => string;
  removeAnchor: (anchorId: string) => void;
  updateAnchor: (anchorId: string, patch: Partial<NodeAnchor>) => void;
  connect: (source: string, target: string) => void;
  disconnect: (edgeId: string) => void;
  setEdges: (edges: AnyLessonPlanEdge[]) => void;
  /** V5-8：自动布局，使用 dagre 计算并应用新位置 */
  autoLayout: (direction?: "TB" | "LR") => void;
}

function reindex(nodes: LessonPlanNode[]): LessonPlanNode[] {
  return nodes.map((n, i) => ({ ...n, order: i }));
}

export const createEditorSlice: StateCreator<
  EditorState,
  [],
  [],
  EditorSlice
> = (set, get) => ({
  planId: "",
  title: "",
  doc: {
    version: 3,
    textbookContentNodeId: "",
    nodes: [],
    edges: [],
    anchors: [],
  },

  setTitle: (title) => {
    get().pushHistory();
    set({ title, isDirty: true });
  },
  setPlanId: (planId) => set({ planId }),

  addNode: (type, position, title) => {
    const id = createId();
    const state = get();
    state.pushHistory(); // V5-2：撤销/重做
    const teachingNodes = state.doc.nodes.filter(
      (n): n is LessonPlanNode => n.type !== "textbook_content",
    );
    const nodeCount = teachingNodes.length;
    const node: LessonPlanNode = {
      id,
      type,
      title: title ?? type,
      data: defaultDataForType(type),
      order: nodeCount,
      position: position ?? {
        x: 80 + (nodeCount % 4) * 280,
        y: 80 + Math.floor(nodeCount / 4) * 200,
      },
    };
    set((s) => ({
      doc: { ...s.doc, nodes: [...s.doc.nodes, node] },
      isDirty: true,
      selectedNodeId: id,
    }));
    return id;
  },

  updateNode: (id, patch) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => ({
      doc: {
        ...s.doc,
        nodes: s.doc.nodes.map((n) =>
          n.id === id
            ? n.type === "textbook_content"
              ? ({ ...n, ...patch } as TextbookContentNode)
              : ({ ...n, ...patch } as LessonPlanNode)
            : n,
        ),
      },
      isDirty: true,
    }));
  },

  updateNodePosition: (id, position) => {
    // V5-2 注：拖拽过程中会产生大量 position 更新，仅在拖拽开始时推一次 history。
    // 调用方应在 onDragStart 时调用 pushHistory，此处不重复推入。
    set((s) => ({
      doc: {
        ...s.doc,
        nodes: s.doc.nodes.map((n) =>
          n.id === id
            ? n.type === "textbook_content"
              ? ({ ...n, position } as TextbookContentNode)
              : ({ ...n, position } as LessonPlanNode)
            : n,
        ),
      },
      isDirty: true,
    }));
  },

  removeNode: (id) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => {
      const remainingTeachingNodes = reindex(
        s.doc.nodes.filter(
          (n): n is LessonPlanNode => n.id !== id && n.type !== "textbook_content",
        ),
      );
      const textbookNode = s.doc.nodes.find(
        (n): n is TextbookContentNode => n.type === "textbook_content",
      );
      const nodes = textbookNode
        ? [textbookNode, ...remainingTeachingNodes]
        : remainingTeachingNodes;
      return {
        doc: {
          ...s.doc,
          nodes,
          edges: s.doc.edges.filter(
            (e) => e.source !== id && e.target !== id,
          ),
          anchors: s.doc.anchors.filter((a) => a.nodeId !== id),
        },
        isDirty: true,
        selectedNodeId: s.selectedNodeId === id ? null : s.selectedNodeId,
      };
    });
  },

  updateTextbookContent: (data) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => ({
      doc: {
        ...s.doc,
        nodes: s.doc.nodes.map((n) =>
          n.type === "textbook_content" && n.id === s.doc.textbookContentNodeId
            ? { ...n, data: { ...n.data, ...data } }
            : n,
        ),
      },
      isDirty: true,
    }));
  },

  getTextbookContentNode: () => {
    const state = get();
    return state.doc.nodes.find(
      (n): n is TextbookContentNode => n.type === "textbook_content",
    );
  },

  addAnchor: ({ nodeId, type, start, end, textPreview }) => {
    const anchorId = createId();
    const state = get();
    state.pushHistory(); // V5-2：撤销/重做
    const textbookNodeId = state.doc.textbookContentNodeId;

    const anchor: NodeAnchor = {
      id: anchorId,
      nodeId,
      type,
      start,
      ...(end !== undefined ? { end } : {}),
      ...(textPreview ? { textPreview } : {}),
    };

    const edge: AnchorEdge = {
      id: `ae_${nodeId}_${textbookNodeId}_${anchorId.slice(0, 6)}`,
      source: nodeId,
      target: textbookNodeId,
      type: "anchor",
      anchorId,
    };

    set((s) => ({
      doc: {
        ...s.doc,
        anchors: [...s.doc.anchors, anchor],
        edges: [...s.doc.edges, edge],
      },
      isDirty: true,
    }));

    return anchorId;
  },

  removeAnchor: (anchorId) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => ({
      doc: {
        ...s.doc,
        anchors: s.doc.anchors.filter((a) => a.id !== anchorId),
        edges: s.doc.edges.filter(
          (e) => !(e.type === "anchor" && e.anchorId === anchorId),
        ),
      },
      isDirty: true,
    }));
  },

  updateAnchor: (anchorId, patch) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => ({
      doc: {
        ...s.doc,
        anchors: s.doc.anchors.map((a) =>
          a.id === anchorId ? { ...a, ...patch } : a,
        ),
      },
      isDirty: true,
    }));
  },

  connect: (source, target) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => {
      if (
        s.doc.edges.some((e) => e.source === source && e.target === target)
      )
        return s;
      const edge: FlowEdge = {
        id: `e_${source}_${target}_${createId().slice(0, 6)}`,
        source,
        target,
        type: "flow",
      };
      return {
        doc: { ...s.doc, edges: [...s.doc.edges, edge] },
        isDirty: true,
      };
    });
  },

  disconnect: (edgeId) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => ({
      doc: {
        ...s.doc,
        edges: s.doc.edges.filter((e) => e.id !== edgeId),
      },
      isDirty: true,
    }));
  },

  setEdges: (edges) => {
    get().pushHistory(); // V5-2：撤销/重做
    set((s) => ({ doc: { ...s.doc, edges }, isDirty: true }));
  },

  autoLayout: (direction = "TB") => {
    const state = get();
    const positions = computeAutoLayout(state.doc.nodes, state.doc.edges, { direction });
    if (positions.size === 0) return;
    state.pushHistory(); // V5-2：撤销/重做
    set((s) => ({
      doc: {
        ...s.doc,
        nodes: s.doc.nodes.map((n) => {
          const p = positions.get(n.id);
          return p
            ? n.type === "textbook_content"
              ? ({ ...n, position: p } as TextbookContentNode)
              : ({ ...n, position: p } as LessonPlanNode)
            : n;
        }),
      },
      isDirty: true,
    }));
  },
});
