import { PermissionKey } from "@capakraken/shared";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
  applyScenario,
  getProjectScenarioBaseline,
  ScenarioSimulationInputSchema,
  simulateScenario,
} from "../router/scenario-procedure-support.js";

const {
  readProjectScenarioBaseline,
  simulateProjectScenario,
  applyProjectScenario,
} = vi.hoisted(() => ({
  readProjectScenarioBaseline: vi.fn(),
  simulateProjectScenario: vi.fn(),
  applyProjectScenario: vi.fn(),
}));

vi.mock("../router/scenario-baseline.js", () => ({
  readProjectScenarioBaseline,
}));

vi.mock("../router/scenario-simulation.js", () => ({
  simulateProjectScenario,
}));

vi.mock("../router/scenario-apply.js", () => ({
  applyProjectScenario,
}));

describe("scenario procedure support", () => {
  beforeEach(() => {
    readProjectScenarioBaseline.mockReset();
    simulateProjectScenario.mockReset();
    applyProjectScenario.mockReset();
  });

  it("enforces at least one scenario change at the schema boundary", () => {
    expect(() => ScenarioSimulationInputSchema.parse({
      projectId: "project_1",
      changes: [],
    })).toThrowError(/at least 1 element/i);
  });

  it("reads the project baseline only after cost permission is present", async () => {
    readProjectScenarioBaseline.mockResolvedValue({ project: { id: "project_1" } });

    const result = await getProjectScenarioBaseline({
      db: { project: {} } as never,
      dbUser: { id: "user_1", systemRole: "USER", permissionOverrides: null },
      permissions: new Set([PermissionKey.VIEW_COSTS]),
    }, {
      projectId: "project_1",
    });

    expect(result).toEqual({ project: { id: "project_1" } });
    expect(readProjectScenarioBaseline).toHaveBeenCalledWith(expect.anything(), "project_1");
  });

  it("delegates pure simulation to the shared simulation service", async () => {
    simulateProjectScenario.mockResolvedValue({ warnings: [] });
    const ctx = { db: { project: {} } };
    const input = {
      projectId: "project_1",
      changes: [{
        resourceId: "resource_1",
        startDate: new Date("2026-04-01T00:00:00.000Z"),
        endDate: new Date("2026-04-02T00:00:00.000Z"),
        hoursPerDay: 8,
      }],
    };

    const result = await simulateScenario(ctx as never, input);

    expect(result).toEqual({ warnings: [] });
    expect(simulateProjectScenario).toHaveBeenCalledWith(ctx.db, input);
  });

  it("passes the acting user id through when applying a scenario", async () => {
    applyProjectScenario.mockResolvedValue({ appliedCount: 2 });
    const ctx = {
      db: { assignment: {} },
      dbUser: { id: "user_1", systemRole: "CONTROLLER", permissionOverrides: null },
    };
    const input = {
      projectId: "project_1",
      changes: [{
        assignmentId: "assignment_1",
        startDate: new Date("2026-04-01T00:00:00.000Z"),
        endDate: new Date("2026-04-03T00:00:00.000Z"),
        hoursPerDay: 6,
      }],
    };

    const result = await applyScenario(ctx as never, input);

    expect(result).toEqual({ appliedCount: 2 });
    expect(applyProjectScenario).toHaveBeenCalledWith(ctx.db, {
      ...input,
      userId: "user_1",
    });
  });
});
