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

const { mockOptima, mockCheckPermissions, mockHandleApiError, mockFail } =
  vi.hoisted(() => ({
    mockOptima: {
      credentialType: {
        fetchMany: vi.fn(),
        create: vi.fn(),
        update: vi.fn(),
        delete: vi.fn(),
      },
      credential: { fetchValueTypes: 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/credential-types +page.server.ts", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.spyOn(console, "error").mockImplementation(() => {});
  });

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

    it("loads credential types with permissions and value types", async () => {
      mockOptima.credentialType.fetchMany.mockResolvedValueOnce({
        data: [{ id: "ct1", name: "SSH Key" }],
      });
      mockCheckPermissions.mockResolvedValueOnce({
        "admin.credential-types.view": true,
      });
      mockOptima.credential.fetchValueTypes.mockResolvedValueOnce({
        data: ["text", "password"],
      });

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

      expect(result).toMatchObject({
        credentialTypes: [{ id: "ct1", name: "SSH Key" }],
        valueTypes: ["text", "password"],
      });
    });
  });

  describe("actions", () => {
    function createFormData(entries: Record<string, string>) {
      return {
        get: (key: string) => entries[key] ?? null,
        getAll: (key: string) => (entries[key] ? [entries[key]] : []),
      };
    }

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

      it("returns 400 when required fields missing", async () => {
        await actions.createCredentialType({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi
              .fn()
              .mockResolvedValue(createFormData({ name: "SSH" })),
          },
        } as any);
        expect(mockFail).toHaveBeenCalledWith(400, {
          message: "Name and permission scope are required.",
        });
      });

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

        const result = await actions.createCredentialType({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi.fn().mockResolvedValue(
              createFormData({
                name: "SSH Key",
                permissionScope: "ssh",
                fields: '[{"name":"key","type":"text"}]',
              }),
            ),
          },
        } as any);

        expect(mockOptima.credentialType.create).toHaveBeenCalledWith("tok", {
          name: "SSH Key",
          permissionScope: "ssh",
          icon: undefined,
          fields: [{ name: "key", type: "text" }],
        });
        expect(result).toEqual({});
      });

      it("returns 400 for invalid fields JSON", async () => {
        await actions.createCredentialType({
          locals: { session: { accessToken: "tok" } },
          request: {
            formData: vi.fn().mockResolvedValue(
              createFormData({
                name: "SSH Key",
                permissionScope: "ssh",
                fields: "bad json",
              }),
            ),
          },
        } as any);
        expect(mockFail).toHaveBeenCalledWith(400, {
          message: "Invalid fields data.",
        });
      });
    });

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

      it("deletes credential type", async () => {
        mockOptima.credentialType.delete.mockResolvedValueOnce({});

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

        expect(mockOptima.credentialType.delete).toHaveBeenCalledWith(
          "tok",
          "ct1",
        );
        expect(result).toEqual({});
      });
    });
  });
});
