// The Lucia project.
// Copyright 2026-2026 DSP, inc.  All rights reserved.
// Authors:
//   imacat.yang@dsp.im (imacat), 2026/03/06

import { describe, it, expect, beforeEach, vi } from "vitest";
import { setActivePinia, createPinia } from "pinia";

// Mock allMapData store (used by createInsightWithPath / createPaths)
vi.mock("@/stores/allMapData", () => ({
  default: () => ({ insights: {} }),
}));

// Mock SVG asset imports
vi.mock("@/assets/capsule1-glow.svg", () => ({ default: "glow1" }));
vi.mock("@/assets/capsule2-glow.svg", () => ({ default: "glow2" }));
vi.mock("@/assets/capsule3-glow.svg", () => ({ default: "glow3" }));
vi.mock("@/assets/capsule4-glow.svg", () => ({ default: "glow4" }));
vi.mock("@/assets/capsule1.svg", () => ({ default: "cap1" }));
vi.mock("@/assets/capsule2.svg", () => ({ default: "cap2" }));
vi.mock("@/assets/capsule3.svg", () => ({ default: "cap3" }));
vi.mock("@/assets/capsule4.svg", () => ({ default: "cap4" }));

import { useMapPathStore } from "@/stores/mapPathStore";

/**
 * Creates a mock Cytoscape node.
 */
function mockNode(label, level = 0) {
  const nodeData = { label, level, nodeImageUrl: "" };
  const classes = new Set();
  const node = {
    data: vi.fn((key, value) => {
      if (value !== undefined) {
        nodeData[key] = value;
        return node;
      }
      return nodeData[key];
    }),
    addClass: vi.fn((cls) => {
      classes.add(cls);
      return node;
    }),
    removeClass: vi.fn((cls) => {
      classes.delete(cls);
      return node;
    }),
    hasClass: (cls) => classes.has(cls),
    outgoers: vi.fn(() => mockCollection([])),
    incomers: vi.fn(() => mockCollection([])),
    edgesTo: vi.fn(() => mockCollection([])),
    edgesWith: vi.fn(() => mockCollection([])),
    _nodeData: nodeData,
    _classes: classes,
  };
  return node;
}

/**
 * Creates a mock Cytoscape edge.
 */
function mockEdge() {
  const classes = new Set();
  const edge = {
    addClass: vi.fn((cls) => {
      classes.add(cls);
      return edge;
    }),
    removeClass: vi.fn((cls) => {
      classes.delete(cls);
      return edge;
    }),
    source: vi.fn(() => mockNode("src")),
    target: vi.fn(() => mockNode("tgt")),
    _classes: classes,
  };
  return edge;
}

/**
 * Creates a mock Cytoscape collection.
 */
function mockCollection(items) {
  const col = {
    forEach: (fn) => items.forEach(fn),
    map: (fn) => items.map(fn),
    filter: (fn) => mockCollection(items.filter(fn)),
    addClass: vi.fn(() => col),
    removeClass: vi.fn(() => col),
    length: items.length,
  };
  return col;
}

/**
 * Creates a mock Cytoscape instance.
 */
function mockCytoscape(nodes = [], edges = []) {
  return {
    nodes: vi.fn(() => mockCollection(nodes)),
    edges: vi.fn(() => mockCollection(edges)),
  };
}

describe("mapPathStore", () => {
  let store;

  beforeEach(() => {
    setActivePinia(createPinia());
    store = useMapPathStore();
    vi.clearAllMocks();
  });

  it("has correct default state", () => {
    expect(store.processOrBPMN).toBe("process");
    expect(store.curveType).toBe("curved");
    expect(store.directionType).toBe("horizontal");
    expect(store.isBPMNOn).toBe(false);
    expect(store.allPaths).toEqual([]);
    expect(store.activeTrace).toBe(0);
    expect(store.lastClickedNode).toBeNull();
  });

  it("setIsBPMNOn updates state", () => {
    store.setIsBPMNOn(true);
    expect(store.isBPMNOn).toBe(true);
    store.setIsBPMNOn(false);
    expect(store.isBPMNOn).toBe(false);
  });

  describe("clearAllHighlight", () => {
    it("removes highlight classes from all nodes and edges", () => {
      const node1 = mockNode("A", 0);
      const edge1 = mockEdge();
      const cy = mockCytoscape([node1], [edge1]);
      store.cytoscape.process.curved.horizontal = cy;

      store.clearAllHighlight();

      expect(cy.edges).toHaveBeenCalled();
      expect(cy.nodes).toHaveBeenCalled();
    });

    it("does not throw when cytoscape is null", () => {
      store.cytoscape.process.curved.horizontal = null;
      expect(() => store.clearAllHighlight()).not.toThrow();
    });
  });

  describe("onNodeClickHighlightEdges", () => {
    it("highlights clicked node and its edges", () => {
      const node = mockNode("Activity", 1);
      const outEdge = mockEdge();
      const inEdge = mockEdge();
      node.outgoers.mockReturnValue(mockCollection([outEdge]));
      node.incomers.mockReturnValue(mockCollection([inEdge]));

      store.cytoscape.process.curved.horizontal = mockCytoscape();

      store.onNodeClickHighlightEdges(node);

      expect(node.addClass).toHaveBeenCalledWith("highlight-node");
      expect(outEdge.addClass).toHaveBeenCalledWith("highlight-edge");
      expect(inEdge.addClass).toHaveBeenCalledWith("highlight-edge");
      expect(store.lastClickedNode).toStrictEqual(node);
    });
  });

  describe("onEdgeClickHighlightNodes", () => {
    it("highlights source and target nodes of clicked edge", () => {
      const src = mockNode("Start", 0);
      const tgt = mockNode("End", 2);
      const edge = mockEdge();
      edge.source.mockReturnValue(src);
      edge.target.mockReturnValue(tgt);

      store.cytoscape.process.curved.horizontal = mockCytoscape();

      store.onEdgeClickHighlightNodes(edge);

      expect(src.addClass).toHaveBeenCalledWith("highlight-node");
      expect(tgt.addClass).toHaveBeenCalledWith("highlight-node");
      expect(edge.addClass).toHaveBeenCalledWith("highlight-edge");
    });
  });
});
