import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import { Transaction } from "../generated/kysely-tailordb";
import { DuplicateRoleNameError, MissingRequiredFieldError } from "../lib/errors.generated";
import { baseRole } from "../testing/fixtures";
import { run } from "./createRole";

describe("createRole", () => {
  // Error cases
  it("throws when name is empty", async () => {
    const { db } = createMockDb<Transaction>();

    const result = await run(db, { name: "" });
    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
    }
  });

  it("throws when name is whitespace only", async () => {
    const { db } = createMockDb<Transaction>();

    const result = await run(db, { name: "   " });
    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(MissingRequiredFieldError);
    }
  });

  it("throws when role name already exists", async () => {
    const { db, spies } = createMockDb<Transaction>();
    spies.select.mockReturnValue(baseRole);

    const result = await run(db, { name: baseRole.name });
    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(DuplicateRoleNameError);
    }
  });

  // Success cases
  it("creates role with valid name", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdRole = {
      ...baseRole,
      id: "new-role-id",
      name: "Viewer",
      description: null,
    };

    spies.select.mockReturnValue(undefined); // No existing role
    spies.insert.mockReturnValue(createdRole);

    const result = await run(db, { name: "Viewer" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.role.name).toBe("Viewer");
    }
    expect(spies.insert).toHaveBeenCalled();
  });

  it("creates role with description", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdRole = {
      ...baseRole,
      id: "new-role-id",
      name: "Editor",
      description: "Can edit content",
    };

    spies.select.mockReturnValue(undefined);
    spies.insert.mockReturnValue(createdRole);

    const result = await run(db, {
      name: "Editor",
      description: "Can edit content",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.role.name).toBe("Editor");
      expect(result.value.role.description).toBe("Can edit content");
    }
  });

  it("passes custom fields through to insert", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdRole = {
      ...baseRole,
      id: "new-role-id",
      name: "Viewer",
      department: "Engineering",
    };

    spies.select.mockReturnValue(undefined);
    spies.insert.mockReturnValue(createdRole);

    await run(db, { name: "Viewer", department: "Engineering" });

    expect(spies.values).toHaveBeenNthCalledWith(
      1,
      expect.objectContaining({ department: "Engineering" }),
    );
  });
});
