import { SystemRole } from "@capakraken/shared";
import { describe, expect, it, vi } from "vitest";
import { webhookRouter } from "../router/webhook.js";
import { createCallerFactory } from "../trpc.js";

const createCaller = createCallerFactory(webhookRouter);

function createContext(
  db: Record<string, unknown>,
  options: {
    role?: SystemRole;
    session?: boolean;
  } = {},
) {
  const { role = SystemRole.USER, session = true } = options;

  return {
    session: session
      ? {
          user: { email: "user@example.com", name: "User", image: null },
          expires: "2099-01-01T00:00:00.000Z",
        }
      : null,
    db: db as never,
    dbUser: session
      ? {
          id: role === SystemRole.ADMIN ? "user_admin" : "user_1",
          systemRole: role,
          permissionOverrides: null,
        }
      : null,
  };
}

describe("webhook router authorization", () => {
  describe("unauthenticated access", () => {
    it("rejects unauthenticated list call with UNAUTHORIZED", async () => {
      const webhookFindMany = vi.fn();
      const caller = createCaller(
        createContext(
          { webhook: { findMany: webhookFindMany } },
          { session: false },
        ),
      );

      await expect(caller.list()).rejects.toMatchObject({
        code: "UNAUTHORIZED",
        message: "Authentication required",
      });

      expect(webhookFindMany).not.toHaveBeenCalled();
    });
  });

  describe("USER role — insufficient for admin-only procedures", () => {
    it("forbids USER from calling list", async () => {
      const webhookFindMany = vi.fn();
      const caller = createCaller(
        createContext({ webhook: { findMany: webhookFindMany } }),
      );

      await expect(caller.list()).rejects.toMatchObject({
        code: "FORBIDDEN",
        message: "Admin role required",
      });

      expect(webhookFindMany).not.toHaveBeenCalled();
    });
  });

  describe("MANAGER role — insufficient for admin-only procedures", () => {
    it("forbids MANAGER from calling list", async () => {
      const webhookFindMany = vi.fn();
      const caller = createCaller(
        createContext(
          { webhook: { findMany: webhookFindMany } },
          { role: SystemRole.MANAGER },
        ),
      );

      await expect(caller.list()).rejects.toMatchObject({
        code: "FORBIDDEN",
        message: "Admin role required",
      });

      expect(webhookFindMany).not.toHaveBeenCalled();
    });

    it("forbids MANAGER from calling create", async () => {
      const webhookCreate = vi.fn();
      const caller = createCaller(
        createContext(
          { webhook: { create: webhookCreate } },
          { role: SystemRole.MANAGER },
        ),
      );

      await expect(
        caller.create({
          name: "My Webhook",
          url: "https://example.com/hook",
          events: ["allocation.created"],
        }),
      ).rejects.toMatchObject({
        code: "FORBIDDEN",
        message: "Admin role required",
      });

      expect(webhookCreate).not.toHaveBeenCalled();
    });
  });

  describe("ADMIN role — full access to all procedures", () => {
    it("allows ADMIN to call list without auth error", async () => {
      const webhookFindMany = vi.fn().mockResolvedValue([]);
      const caller = createCaller(
        createContext(
          { webhook: { findMany: webhookFindMany } },
          { role: SystemRole.ADMIN },
        ),
      );

      const result = await caller.list();

      expect(result).toEqual([]);
      expect(webhookFindMany).toHaveBeenCalledTimes(1);
    });

    it("allows ADMIN to call create without auth error", async () => {
      const createdWebhook = {
        id: "webhook_1",
        name: "Slack Notifications",
        url: "https://hooks.slack.com/services/test",
        secret: null,
        events: ["allocation.created"],
        isActive: true,
        createdAt: new Date("2024-01-01"),
        updatedAt: new Date("2024-01-01"),
      };
      const webhookCreate = vi.fn().mockResolvedValue(createdWebhook);
      const auditLogCreate = vi.fn().mockResolvedValue({});

      const caller = createCaller(
        createContext(
          {
            webhook: { create: webhookCreate },
            auditLog: { create: auditLogCreate },
          },
          { role: SystemRole.ADMIN },
        ),
      );

      const result = await caller.create({
        name: "Slack Notifications",
        url: "https://hooks.slack.com/services/test",
        events: ["allocation.created"],
      });

      expect(result).toMatchObject({ id: "webhook_1", name: "Slack Notifications" });
      expect(webhookCreate).toHaveBeenCalledTimes(1);
    });
  });
});
