import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import { Transaction } from "../generated/kysely-tailordb";
import {
  DuplicatePermissionKeyError,
  InvalidPermissionKeyFormatError,
  MissingRequiredFieldError,
} from "../lib/errors.generated";
import { basePermission } from "../testing/fixtures";
import { run } from "./createPermission";

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

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

  it("throws when key format is invalid (no colon)", async () => {
    const { db } = createMockDb<Transaction>();

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

  it("throws when key format is invalid (multiple colons)", async () => {
    const { db } = createMockDb<Transaction>();

    const result = await run(db, { key: "resource:action:extra" });
    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(InvalidPermissionKeyFormatError);
    }
  });

  it("throws when key format is invalid (empty resource)", async () => {
    const { db } = createMockDb<Transaction>();

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

  it("throws when key format is invalid (empty action)", async () => {
    const { db } = createMockDb<Transaction>();

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

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

    const result = await run(db, { key: basePermission.key });
    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(DuplicatePermissionKeyError);
    }
  });

  // Success cases
  it("creates permission with valid key", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdPermission = {
      ...basePermission,
      id: "new-permission-id",
      key: "users:read",
      description: null,
    };

    spies.select.mockReturnValue(undefined); // No existing permission
    spies.insert.mockReturnValue(createdPermission);

    const result = await run(db, { key: "users:read" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.permission.key).toBe("users:read");
    }
    expect(spies.insert).toHaveBeenCalled();
  });

  it("creates permission with description", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdPermission = {
      ...basePermission,
      id: "new-permission-id",
      key: "users:write",
      description: "Permission to write users",
    };

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

    const result = await run(db, {
      key: "users:write",
      description: "Permission to write users",
    });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.permission.key).toBe("users:write");
      expect(result.value.permission.description).toBe("Permission to write users");
    }
  });

  it("passes custom fields through to insert", async () => {
    const { db, spies } = createMockDb<Transaction>();
    const createdPermission = {
      ...basePermission,
      id: "new-permission-id",
      key: "users:read",
      module: "user-management",
    };

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

    await run(db, { key: "users:read", module: "user-management" });

    expect(spies.values).toHaveBeenNthCalledWith(
      1,
      expect.objectContaining({ module: "user-management" }),
    );
  });
});
