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

vi.mock("../lib/holiday-availability.js", () => ({
  asHolidayResolverDb: vi.fn((db) => db),
  getResolvedCalendarHolidays: vi.fn(),
}));

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

import {
  asHolidayResolverDb,
  getResolvedCalendarHolidays,
} from "../lib/holiday-availability.js";
import { loadTimelineEntriesReadModel } from "../router/timeline-read-shared.js";
import {
  loadTimelineHolidayOverlays,
  loadTimelineHolidayOverlaysForReadModel,
} from "../router/timeline-holiday-load-support.js";

const asHolidayResolverDbMock = vi.mocked(asHolidayResolverDb);
const getResolvedCalendarHolidaysMock = vi.mocked(getResolvedCalendarHolidays);
const loadTimelineEntriesReadModelMock = vi.mocked(loadTimelineEntriesReadModel);

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

  it("loads the timeline read model before building holiday overlays", async () => {
    const resourceFindMany = vi
      .fn()
      .mockResolvedValueOnce([
        {
          id: "resource_1",
          countryId: "country_1",
          federalState: "BY",
          metroCityId: "city_1",
          country: { code: "DE", name: "Germany" },
          metroCity: { name: "Munich" },
        },
      ]);
    const db = { resource: { findMany: resourceFindMany } } as never;
    const input = {
      startDate: new Date("2026-04-01T00:00:00.000Z"),
      endDate: new Date("2026-04-07T00:00:00.000Z"),
    };

    loadTimelineEntriesReadModelMock.mockResolvedValueOnce({
      assignments: [{ resourceId: "resource_1" }],
    } as never);
    getResolvedCalendarHolidaysMock.mockResolvedValueOnce([
      {
        date: "2026-04-03",
        name: "Holiday",
        scope: "COUNTRY",
        calendarName: "System",
        sourceType: "BUILTIN",
      },
    ] as never);

    await expect(loadTimelineHolidayOverlays(db, input)).resolves.toEqual([
      expect.objectContaining({
        id: "calendar-holiday:resource_1:2026-04-03",
        resourceId: "resource_1",
        note: "Holiday",
      }),
    ]);

    expect(loadTimelineEntriesReadModelMock).toHaveBeenCalledWith(db, input);
    expect(asHolidayResolverDbMock).toHaveBeenCalledWith(db);
  });

  it("returns an empty list when no resource ids can be resolved", async () => {
    const resourceFindMany = vi.fn();
    const db = { resource: { findMany: resourceFindMany } } as never;

    await expect(
      loadTimelineHolidayOverlaysForReadModel(
        db,
        {
          startDate: new Date("2026-04-01T00:00:00.000Z"),
          endDate: new Date("2026-04-07T00:00:00.000Z"),
        },
        {
          assignments: [{ resourceId: null }],
        } as never,
      ),
    ).resolves.toEqual([]);

    expect(resourceFindMany).not.toHaveBeenCalled();
    expect(getResolvedCalendarHolidaysMock).not.toHaveBeenCalled();
  });

  it("resolves holidays for matching resources and returns overlays sorted by resource and date", async () => {
    const resourceFindMany = vi
      .fn()
      .mockResolvedValueOnce([{ id: "resource_2" }])
      .mockResolvedValueOnce([
        {
          id: "resource_2",
          countryId: "country_2",
          federalState: null,
          metroCityId: null,
          country: { code: "US", name: "United States" },
          metroCity: null,
        },
        {
          id: "resource_1",
          countryId: "country_1",
          federalState: "BY",
          metroCityId: "city_1",
          country: { code: "DE", name: "Germany" },
          metroCity: { name: "Munich" },
        },
      ]);
    const db = { resource: { findMany: resourceFindMany } } as never;

    getResolvedCalendarHolidaysMock
      .mockResolvedValueOnce([
        {
          date: "2026-04-04",
          name: "Holiday B",
          scope: "COUNTRY",
          calendarName: "System",
          sourceType: "BUILTIN",
        },
      ] as never)
      .mockResolvedValueOnce([
        {
          date: "2026-04-03",
          name: "Holiday A",
          scope: "CITY",
          calendarName: "Munich",
          sourceType: "CUSTOM",
        },
      ] as never);

    await expect(
      loadTimelineHolidayOverlaysForReadModel(
        db,
        {
          startDate: new Date("2026-04-01T00:00:00.000Z"),
          endDate: new Date("2026-04-07T00:00:00.000Z"),
          chapters: ["3D"],
        },
        {
          assignments: [{ resourceId: "resource_1" }],
        } as never,
      ),
    ).resolves.toEqual([
      expect.objectContaining({
        id: "calendar-holiday:resource_1:2026-04-03",
        resourceId: "resource_1",
        note: "Holiday A",
        countryCode: "DE",
      }),
      expect.objectContaining({
        id: "calendar-holiday:resource_2:2026-04-04",
        resourceId: "resource_2",
        note: "Holiday B",
        countryCode: "US",
      }),
    ]);

    expect(resourceFindMany).toHaveBeenNthCalledWith(1, {
      where: { chapter: { in: ["3D"] } },
      select: { id: true },
    });
    expect(resourceFindMany).toHaveBeenNthCalledWith(2, {
      where: { id: { in: ["resource_1", "resource_2"] } },
      select: {
        id: true,
        countryId: true,
        federalState: true,
        metroCityId: true,
        country: { select: { code: true, name: true } },
        metroCity: { select: { name: true } },
      },
    });
  });
});
