import { SystemRole } from "@capakraken/shared";
import { VacationType } from "@capakraken/db";
import { describe, expect, it, vi } from "vitest";
import { vacationRouter } from "../router/vacation.js";
import { createCallerFactory } from "../trpc.js";

const createCaller = createCallerFactory(vacationRouter);

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" : role === SystemRole.MANAGER ? "user_mgr" : "user_1",
          systemRole: role,
          permissionOverrides: null,
        }
      : null,
  };
}

function previewInput(resourceId: string) {
  return {
    resourceId,
    type: VacationType.ANNUAL,
    startDate: new Date("2026-08-10T00:00:00.000Z"),
    endDate: new Date("2026-08-12T00:00:00.000Z"),
  };
}

describe("vacation router authorization", () => {
  it("requires authentication for preview requests", async () => {
    const resourceFindFirst = vi.fn();
    const resourceFindUnique = vi.fn();
    const caller = createCaller(createContext({
      resource: {
        findFirst: resourceFindFirst,
        findUnique: resourceFindUnique,
      },
      vacation: {
        findMany: vi.fn(),
      },
    }, { session: false }));

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

    expect(resourceFindFirst).not.toHaveBeenCalled();
    expect(resourceFindUnique).not.toHaveBeenCalled();
  });

  it("forbids regular users from previewing another resource's request", async () => {
    const resourceFindUnique = vi.fn();
    const vacationFindMany = vi.fn();
    const caller = createCaller(createContext({
      resource: {
        findFirst: vi.fn().mockResolvedValue({ id: "res_own" }),
        findUnique: resourceFindUnique,
      },
      vacation: {
        findMany: vacationFindMany,
      },
    }));

    await expect(caller.previewRequest(previewInput("res_other"))).rejects.toMatchObject({
      code: "FORBIDDEN",
      message: "You can only view vacation data for your own resource",
    });

    expect(resourceFindUnique).not.toHaveBeenCalled();
    expect(vacationFindMany).not.toHaveBeenCalled();
  });

  it("allows regular users to preview their own requests", async () => {
    const resourceFindUnique = vi.fn().mockResolvedValue({
      federalState: "BY",
      countryId: "country_de",
      metroCityId: null,
      country: { code: "DE", name: "Germany" },
      metroCity: null,
    });
    const vacationFindMany = vi.fn().mockResolvedValue([]);
    const caller = createCaller(createContext({
      resource: {
        findFirst: vi.fn().mockResolvedValue({ id: "res_own" }),
        findUnique: resourceFindUnique,
      },
      holidayCalendar: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      vacation: {
        findMany: vacationFindMany,
      },
    }));

    const result = await caller.previewRequest(previewInput("res_own"));

    expect(result.requestedDays).toBe(3);
    expect(resourceFindUnique).toHaveBeenCalledWith({
      where: { id: "res_own" },
      select: {
        federalState: true,
        countryId: true,
        metroCityId: true,
        country: { select: { code: true, name: true } },
        metroCity: { select: { name: true } },
      },
    });
  });

  it("allows managers to preview requests for other resources", async () => {
    const resourceFindFirst = vi.fn();
    const resourceFindUnique = vi.fn().mockResolvedValue({
      federalState: "BY",
      countryId: "country_de",
      metroCityId: null,
      country: { code: "DE", name: "Germany" },
      metroCity: null,
    });
    const caller = createCaller(createContext({
      resource: {
        findFirst: resourceFindFirst,
        findUnique: resourceFindUnique,
      },
      holidayCalendar: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      vacation: {
        findMany: vi.fn().mockResolvedValue([]),
      },
    }, { role: SystemRole.MANAGER }));

    const result = await caller.previewRequest(previewInput("res_other"));

    expect(result.requestedDays).toBe(3);
    expect(resourceFindFirst).not.toHaveBeenCalled();
    expect(resourceFindUnique).toHaveBeenCalledWith({
      where: { id: "res_other" },
      select: {
        federalState: true,
        countryId: true,
        metroCityId: true,
        country: { select: { code: true, name: true } },
        metroCity: { select: { name: true } },
      },
    });
  });
});
