import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { baseRootGroup, baseChildGroup, baseGrandchildGroup } from "../testing/fixtures";
import { run } from "./detectCircularReference";

describe("detectCircularReference", () => {
  it("detects self-reference as circular", async () => {
    const { db } = createMockDb<DB>();

    const result = await run(db, { groupId: "group-1", newParentId: "group-1" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.isCircular).toBe(true);
    }
  });

  it("detects moving group under its child as circular", async () => {
    const { db, spies } = createMockDb<DB>();
    // Walking up from group-2 (child): parent is group-1 (the group being moved)
    spies.select.mockReturnValueOnce(baseChildGroup); // group-2's parent is group-1

    const result = await run(db, { groupId: "group-1", newParentId: "group-2" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.isCircular).toBe(true);
    }
  });

  it("detects moving group under its grandchild as circular", async () => {
    const { db, spies } = createMockDb<DB>();
    // Walking up from group-3: parent is group-2
    spies.select
      .mockReturnValueOnce(baseGrandchildGroup) // group-3's parent is group-2
      .mockReturnValueOnce(baseChildGroup); // group-2's parent is group-1

    const result = await run(db, { groupId: "group-1", newParentId: "group-3" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.isCircular).toBe(true);
    }
  });

  it("returns not circular for valid move", async () => {
    const { db, spies } = createMockDb<DB>();
    // Walking up from group-4 (root, no parent)
    spies.select.mockReturnValueOnce({
      ...baseRootGroup,
      id: "group-4",
      parentAccountGroupId: null,
    });

    const result = await run(db, { groupId: "group-2", newParentId: "group-4" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.isCircular).toBe(false);
    }
  });

  it("returns not circular when new parent is root", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseRootGroup); // group-1 is root (parentAccountGroupId: null)

    const result = await run(db, { groupId: "group-2", newParentId: "group-1" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.isCircular).toBe(false);
    }
  });

  it("handles broken ancestor chain gracefully (not circular)", async () => {
    const { db, spies } = createMockDb<DB>();
    // Parent not found in DB
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { groupId: "group-1", newParentId: "group-unknown" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.isCircular).toBe(false);
    }
  });
});
