import { beforeEach, describe, expect, it, vi } from "vitest";

const { mockOptima, mockCheckPermissions, mockHandleApiError, mockFail } =
  vi.hoisted(() => ({
    mockOptima: {
      role: {
        fetchMany: vi.fn(),
        fetchUsers: vi.fn(),
        create: vi.fn(),
        update: vi.fn(),
        delete: vi.fn(),
      },
      permission: { fetchCategorized: vi.fn() },
    },
    mockCheckPermissions: vi.fn(),
    mockHandleApiError: vi.fn(),
    mockFail: vi.fn((status: number, data: any) => ({
      status,
      data,
    })),
  }));

vi.mock("$lib", () => ({ optima: mockOptima }));
vi.mock("$lib/permissions", () => ({
  checkPermissions: mockCheckPermissions,
}));
vi.mock("$lib/optima-api/errorHandler", () => ({
  handleApiError: mockHandleApiError,
}));
vi.mock("@sveltejs/kit", () => ({
  fail: mockFail,
}));

import { load, actions } from "./+page.server";

describe("admin/roles +page.server.ts", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe("load", () => {
    it("returns empty data when no token", async () => {
      const result = await load({ locals: {} } as any);
      expect(result).toEqual({
        roles: [],
        permissions: {},
        permissionNodes: {},
      });
    });

    it("loads roles with users and permissions", async () => {
      mockOptima.role.fetchMany.mockResolvedValueOnce({
        data: [{ id: "r1", title: "Admin" }],
      });
      mockCheckPermissions.mockResolvedValueOnce({
        "admin.roles.view": true,
      });
      mockOptima.permission.fetchCategorized.mockResolvedValueOnce({
        data: { category1: ["perm.a"] },
      });
      mockOptima.role.fetchUsers.mockResolvedValueOnce({
        data: [{ id: "u1" }],
      });

      const result = await load({
        locals: { session: { accessToken: "tok" } },
      } as any);

      expect(result).toMatchObject({
        roles: [
          expect.objectContaining({
            id: "r1",
            users: [{ id: "u1" }],
          }),
        ],
        permissionNodes: { category1: ["perm.a"] },
      });
    });
  });

  describe("actions", () => {
    function createFormData(entries: Record<string, string | string[]>) {
      const fd = new Map<string, string | string[]>();
      return {
        get: (key: string) => {
          const val = entries[key];
          return Array.isArray(val) ? val[0] : (val ?? null);
        },
        getAll: (key: string) => {
          const val = entries[key];
          return Array.isArray(val) ? val : val ? [val] : [];
        },
      };
    }

    describe("createRole", () => {
      it("returns 401 when no token", async () => {
        const result = await actions.createRole({
          locals: {},
          request: {
            formData: vi.fn().mockResolvedValue(createFormData({})),
          },
        } as any);
        expect(mockFail).toHaveBeenCalledWith(401, {
          message: "Not authenticated.",
        });
      });

      it("returns 400 when title is missing", async () => {
        await actions.createRole({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi
              .fn()
              .mockResolvedValue(createFormData({ moniker: "admin" })),
          },
        } as any);
        expect(mockFail).toHaveBeenCalledWith(400, {
          message: "Title and moniker are required.",
        });
      });

      it("creates role successfully", async () => {
        mockOptima.role.create.mockResolvedValueOnce({});

        const result = await actions.createRole({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi.fn().mockResolvedValue(
              createFormData({
                title: "Admin",
                moniker: "admin",
                permissions: ["perm.a", "perm.b"],
              }),
            ),
          },
        } as any);

        expect(mockOptima.role.create).toHaveBeenCalledWith("tok", {
          title: "Admin",
          moniker: "admin",
          permissions: ["perm.a", "perm.b"],
        });
        expect(result).toEqual({});
      });
    });

    describe("deleteRole", () => {
      it("returns 400 when id is missing", async () => {
        await actions.deleteRole({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi.fn().mockResolvedValue(createFormData({})),
          },
        } as any);
        expect(mockFail).toHaveBeenCalledWith(400, {
          message: "Role ID is required.",
        });
      });

      it("deletes role successfully", async () => {
        mockOptima.role.delete.mockResolvedValueOnce({});

        const result = await actions.deleteRole({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi.fn().mockResolvedValue(createFormData({ id: "r1" })),
          },
        } as any);

        expect(mockOptima.role.delete).toHaveBeenCalledWith("tok", "r1");
        expect(result).toEqual({});
      });
    });
  });
});
