import { describe, test, expect, mock, beforeEach } from "bun:test";

// Mock the user controller's hasPermission
const mockHasPermission = mock(() => Promise.resolve(true));

const mockUserController = {
  hasPermission: mockHasPermission,
};

describe("processObjectValuePerms", () => {
  // Import after mock setup
  const { processObjectValuePerms, processObjectPermMap } =
    require("../../src/modules/permission-utils/processObjectPermissions") as typeof import("../../src/modules/permission-utils/processObjectPermissions");

  beforeEach(() => {
    mockHasPermission.mockReset();
  });

  test("returns only fields user has permission for", async () => {
    let callCount = 0;
    mockHasPermission.mockImplementation(() => {
      callCount++;
      // Allow field "name" but deny "secret"
      return Promise.resolve(callCount === 1);
    });

    const obj = { name: "Test", secret: "hidden" };
    const result = await processObjectValuePerms(
      obj,
      "scope",
      mockUserController as any,
    );
    // First call: scope.name → true, second: scope.secret → false
    expect(result.name).toBe("Test");
    expect(result.secret).toBeUndefined();
  });

  test("returns empty object when user has no permissions", async () => {
    mockHasPermission.mockResolvedValue(false);
    const obj = { a: 1, b: 2, c: 3 };
    const result = await processObjectValuePerms(
      obj,
      "test",
      mockUserController as any,
    );
    expect(Object.keys(result)).toHaveLength(0);
  });

  test("returns full object when user has all permissions", async () => {
    mockHasPermission.mockResolvedValue(true);
    const obj = { x: "hello", y: 42 };
    const result = await processObjectValuePerms(
      obj,
      "test",
      mockUserController as any,
    );
    expect(result).toEqual({ x: "hello", y: 42 });
  });

  test("checks permission with correct scope.key pattern", async () => {
    mockHasPermission.mockResolvedValue(true);
    const obj = { fieldA: 1 };
    await processObjectValuePerms(obj, "myScope", mockUserController as any);
    expect(mockHasPermission).toHaveBeenCalledWith("myScope.fieldA");
  });
});

describe("processObjectPermMap", () => {
  const { processObjectPermMap } =
    require("../../src/modules/permission-utils/processObjectPermissions") as typeof import("../../src/modules/permission-utils/processObjectPermissions");

  beforeEach(() => {
    mockHasPermission.mockReset();
  });

  test("returns boolean map for each key", async () => {
    let idx = 0;
    mockHasPermission.mockImplementation(() => {
      idx++;
      return Promise.resolve(idx % 2 === 1); // true, false, true, ...
    });

    const obj = { a: "x", b: "y", c: "z" };
    const result = await processObjectPermMap(
      obj,
      "scope",
      mockUserController as any,
    );
    expect(result.a).toBe(true);
    expect(result.b).toBe(false);
    expect(result.c).toBe(true);
  });

  test("all true when user has all permissions", async () => {
    mockHasPermission.mockResolvedValue(true);
    const obj = { foo: 1, bar: 2 };
    const result = await processObjectPermMap(
      obj,
      "s",
      mockUserController as any,
    );
    expect(result.foo).toBe(true);
    expect(result.bar).toBe(true);
  });

  test("all false when user has no permissions", async () => {
    mockHasPermission.mockResolvedValue(false);
    const obj = { foo: 1, bar: 2 };
    const result = await processObjectPermMap(
      obj,
      "s",
      mockUserController as any,
    );
    expect(result.foo).toBe(false);
    expect(result.bar).toBe(false);
  });
});
