import { OrderType, PermissionKey, SystemRole } from "@capakraken/shared";
import { describe, expect, it, vi } from "vitest";
import { scenarioRouter } from "../router/scenario.js";
import { createCallerFactory } from "../trpc.js";

vi.mock("../lib/resource-capacity.js", () => ({
  calculateEffectiveAvailableHours: vi.fn(),
  calculateEffectiveBookedHours: vi.fn(),
  loadResourceDailyAvailabilityContexts: vi.fn().mockResolvedValue(new Map()),
}));

const createCaller = createCallerFactory(scenarioRouter);

function createAuthenticatedCaller(db: Record<string, unknown>) {
  return createCaller({
    session: {
      user: { email: "user@example.com", name: "User", image: null },
      expires: "2099-01-01T00:00:00.000Z",
    },
    db: db as never,
    dbUser: {
      id: "user_1",
      systemRole: SystemRole.USER,
      permissionOverrides: null,
    },
  });
}

function createProtectedCallerWithOverrides(
  db: Record<string, unknown>,
  overrides: { granted?: PermissionKey[]; denied?: PermissionKey[] } | null,
) {
  return createCaller({
    session: {
      user: { email: "user@example.com", name: "User", image: null },
      expires: "2099-01-01T00:00:00.000Z",
    },
    db: db as never,
    dbUser: {
      id: "user_1",
      systemRole: SystemRole.USER,
      permissionOverrides: overrides,
    },
  });
}

describe("scenario router authorization", () => {
  it("requires planning read access for getProjectBaseline", async () => {
    const caller = createAuthenticatedCaller({});

    await expect(caller.getProjectBaseline({
      projectId: "project_1",
    })).rejects.toMatchObject({
      code: "FORBIDDEN",
      message: "Planning read access required",
    });
  });

  it("does not treat viewCosts as a substitute for viewPlanning on getProjectBaseline", async () => {
    const caller = createProtectedCallerWithOverrides({}, {
      granted: [PermissionKey.VIEW_COSTS],
    });

    await expect(caller.getProjectBaseline({
      projectId: "project_1",
    })).rejects.toMatchObject({
      code: "FORBIDDEN",
      message: "Planning read access required",
    });
  });

  it("requires viewCosts for getProjectBaseline", async () => {
    const caller = createProtectedCallerWithOverrides({}, {
      granted: [PermissionKey.VIEW_PLANNING],
    });

    await expect(caller.getProjectBaseline({
      projectId: "project_1",
    })).rejects.toMatchObject({
      code: "FORBIDDEN",
      message: `Permission required: ${PermissionKey.VIEW_COSTS}`,
    });
  });

  it("allows getProjectBaseline with both planning and cost permissions", async () => {
    const project = {
      id: "project_1",
      name: "Project One",
      shortCode: "PRJ-001",
      startDate: new Date("2026-04-01T00:00:00.000Z"),
      endDate: new Date("2026-04-30T00:00:00.000Z"),
      budgetCents: 125_000_00,
      orderType: OrderType.CHARGEABLE,
    };
    const db = {
      project: {
        findUnique: vi.fn().mockResolvedValue(project),
      },
      assignment: {
        findMany: vi.fn().mockResolvedValue([]),
      },
      demandRequirement: {
        findMany: vi.fn().mockResolvedValue([]),
      },
    };

    const caller = createProtectedCallerWithOverrides(db, {
      granted: [PermissionKey.VIEW_PLANNING, PermissionKey.VIEW_COSTS],
    });
    const result = await caller.getProjectBaseline({
      projectId: "project_1",
    });

    expect(db.project.findUnique).toHaveBeenCalledWith({
      where: { id: "project_1" },
      select: {
        id: true,
        name: true,
        shortCode: true,
        startDate: true,
        endDate: true,
        budgetCents: true,
        orderType: true,
      },
    });
    expect(result).toEqual({
      project,
      assignments: [],
      demands: [],
      totalCostCents: 0,
      totalHours: 0,
      budgetCents: project.budgetCents,
    });
  });
});
