import { TRPCError } from "@trpc/server";
import { describe, expect, it, vi } from "vitest";
import {
  appendZeroAllocationCount,
  assertRoleNameAvailable,
  buildRoleCreateData,
  buildRoleListWhere,
  buildRoleUpdateData,
  findRoleByIdentifier,
} from "../router/role-support.js";

describe("role support", () => {
  it("builds list filters", () => {
    expect(buildRoleListWhere({
      isActive: true,
      search: "FX",
    })).toEqual({
      isActive: true,
      name: { contains: "FX", mode: "insensitive" },
    });
  });

  it("resolves a role by case-insensitive name fallback", async () => {
    const db = {
      role: {
        findUnique: vi.fn()
          .mockResolvedValueOnce(null)
          .mockResolvedValueOnce(null),
        findFirst: vi.fn()
          .mockResolvedValueOnce({ id: "role_fx", name: "FX" }),
      },
    } as never;

    const result = await findRoleByIdentifier<{ id: string; name: string }>(
      db,
      " fx ",
      { id: true, name: true },
    );

    expect(result).toEqual({ id: "role_fx", name: "FX" });
    expect(db.role.findUnique).toHaveBeenNthCalledWith(1, {
      where: { id: "fx" },
      select: { id: true, name: true },
    });
  });

  it("throws when no identifier match exists", async () => {
    const db = {
      role: {
        findUnique: vi.fn().mockResolvedValue(null),
        findFirst: vi.fn().mockResolvedValue(null),
      },
    } as never;

    await expect(findRoleByIdentifier(db, "missing", { id: true })).rejects.toBeInstanceOf(TRPCError);
  });

  it("builds create and sparse update payloads", () => {
    expect(buildRoleCreateData({
      name: "FX",
      description: undefined,
      color: "#111111",
    })).toEqual({
      name: "FX",
      description: null,
      color: "#111111",
    });

    expect(buildRoleUpdateData({
      description: "Updated",
      isActive: false,
    })).toEqual({
      description: "Updated",
      isActive: false,
    });
  });

  it("appends a zero allocation count on create responses", () => {
    expect(appendZeroAllocationCount({
      id: "role_fx",
      _count: { resourceRoles: 2 },
    })).toEqual({
      id: "role_fx",
      _count: { resourceRoles: 2, allocations: 0 },
    });
  });

  it("rejects duplicate role names outside the ignored id", async () => {
    const db = {
      role: {
        findUnique: vi.fn().mockResolvedValue({ id: "role_existing", name: "FX" }),
      },
    } as never;

    await expect(assertRoleNameAvailable(db, "FX")).rejects.toMatchObject({
      code: "CONFLICT",
      message: 'Role "FX" already exists',
    });
  });
});
