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

vi.mock("@capakraken/application", () => ({
  listAssignmentBookings: vi.fn(),
}));

vi.mock("../router/project-planning-read-model.js", () => ({
  loadProjectPlanningReadModel: vi.fn(),
}));

vi.mock("../router/timeline-project-query-support.js", () => ({
  findTimelineProjectOrThrow: vi.fn(),
  projectShiftContextSelect: { id: true },
  timelineProjectContextSelect: { id: true },
}));

vi.mock("../router/timeline-shift-planning.js", () => ({
  buildTimelineShiftPlan: vi.fn(),
}));

vi.mock("../router/timeline-project-read-support.js", () => ({
  buildTimelineShiftValidationBookings: vi.fn(),
}));

vi.mock("../router/timeline-read-shared.js", async () => {
  const actual = await vi.importActual<typeof import("../router/timeline-read-shared.js")>(
    "../router/timeline-read-shared.js",
  );

  return {
    ...actual,
    getAssignmentResourceIds: vi.fn(),
  };
});

vi.mock("../router/timeline-shift-support.js", () => ({
  buildTimelineProjectShiftValidation: vi.fn(),
}));

import { listAssignmentBookings } from "@capakraken/application";
import { loadProjectPlanningReadModel } from "../router/project-planning-read-model.js";
import {
  loadProjectShiftContext,
  loadTimelineProjectContext,
  previewTimelineProjectShift,
} from "../router/timeline-project-load-support.js";
import { findTimelineProjectOrThrow } from "../router/timeline-project-query-support.js";
import { buildTimelineShiftPlan } from "../router/timeline-shift-planning.js";
import { buildTimelineShiftValidationBookings } from "../router/timeline-project-read-support.js";
import { getAssignmentResourceIds } from "../router/timeline-read-shared.js";
import { buildTimelineProjectShiftValidation } from "../router/timeline-shift-support.js";

const findTimelineProjectOrThrowMock = vi.mocked(findTimelineProjectOrThrow);
const loadProjectPlanningReadModelMock = vi.mocked(loadProjectPlanningReadModel);
const listAssignmentBookingsMock = vi.mocked(listAssignmentBookings);
const getAssignmentResourceIdsMock = vi.mocked(getAssignmentResourceIds);
const buildTimelineShiftValidationBookingsMock = vi.mocked(buildTimelineShiftValidationBookings);
const buildTimelineShiftPlanMock = vi.mocked(buildTimelineShiftPlan);
const buildTimelineProjectShiftValidationMock = vi.mocked(buildTimelineProjectShiftValidation);

describe("timeline project load support", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("loads shift context with booking windows when assignment resources exist", async () => {
    findTimelineProjectOrThrowMock.mockResolvedValueOnce({ id: "project_1" } as never);
    loadProjectPlanningReadModelMock.mockResolvedValueOnce({
      demandRequirements: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
      readModel: {
        allocations: [],
        demands: [],
        assignments: [{ resourceId: "resource_1" }],
      },
    } as never);
    getAssignmentResourceIdsMock.mockReturnValueOnce(["resource_1"]);
    listAssignmentBookingsMock.mockResolvedValueOnce([{ id: "booking_1" }] as never);
    buildTimelineShiftValidationBookingsMock.mockReturnValueOnce([{ id: "window_1" }] as never);
    buildTimelineShiftPlanMock.mockReturnValueOnce({ shifts: 1 } as never);

    await expect(loadProjectShiftContext({} as never, "project_1")).resolves.toEqual({
      project: { id: "project_1" },
      demandRequirements: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
      shiftPlan: { shifts: 1 },
    });

    expect(listAssignmentBookingsMock).toHaveBeenCalledWith({}, {
      resourceIds: ["resource_1"],
    });
    expect(buildTimelineShiftValidationBookingsMock).toHaveBeenCalledWith([{ id: "booking_1" }]);
    expect(buildTimelineShiftPlanMock).toHaveBeenCalledWith({
      demandRequirements: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
      allAssignmentWindows: [{ id: "window_1" }],
    });
  });

  it("loads project context and skips booking lookup without assignment resources", async () => {
    findTimelineProjectOrThrowMock.mockResolvedValueOnce({
      id: "project_1",
      startDate: new Date("2026-04-01T00:00:00.000Z"),
      endDate: new Date("2026-04-10T00:00:00.000Z"),
    } as never);
    loadProjectPlanningReadModelMock.mockResolvedValueOnce({
      readModel: {
        allocations: [{ id: "allocation_1" }],
        demands: [{ id: "demand_1" }],
        assignments: [{ id: "assignment_1" }],
      },
    } as never);
    getAssignmentResourceIdsMock.mockReturnValueOnce([]);

    await expect(loadTimelineProjectContext({} as never, "project_1")).resolves.toEqual({
      project: {
        id: "project_1",
        startDate: new Date("2026-04-01T00:00:00.000Z"),
        endDate: new Date("2026-04-10T00:00:00.000Z"),
      },
      allocations: [{ id: "allocation_1" }],
      demands: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
      allResourceAllocations: [],
      resourceIds: [],
    });

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

  it("builds project shift previews from loaded context", async () => {
    findTimelineProjectOrThrowMock.mockResolvedValueOnce({ id: "project_1" } as never);
    loadProjectPlanningReadModelMock.mockResolvedValueOnce({
      demandRequirements: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
      readModel: {
        allocations: [],
        demands: [],
        assignments: [{ resourceId: null }],
      },
    } as never);
    getAssignmentResourceIdsMock.mockReturnValueOnce([]);
    buildTimelineShiftPlanMock.mockReturnValueOnce({ shifts: [] } as never);
    buildTimelineProjectShiftValidationMock.mockReturnValueOnce({ ok: true } as never);

    await expect(
      previewTimelineProjectShift({} as never, {
        projectId: "project_1",
        newStartDate: new Date("2026-04-02T00:00:00.000Z"),
        newEndDate: new Date("2026-04-08T00:00:00.000Z"),
      }),
    ).resolves.toEqual({ ok: true });

    expect(buildTimelineProjectShiftValidationMock).toHaveBeenCalledWith({
      context: {
        project: { id: "project_1" },
        demandRequirements: [{ id: "demand_1" }],
        assignments: [{ id: "assignment_1" }],
        shiftPlan: { shifts: [] },
      },
      newStartDate: new Date("2026-04-02T00:00:00.000Z"),
      newEndDate: new Date("2026-04-08T00:00:00.000Z"),
    });
  });
});
