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

vi.mock("../lib/anonymization.js", () => ({
  getAnonymizationDirectory: vi.fn(),
}));

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

vi.mock("../router/timeline-entry-response-support.js", () => ({
  buildTimelineEntriesDetailResponse: vi.fn(),
  buildTimelineEntriesViewResponse: vi.fn(),
}));

vi.mock("../router/timeline-holiday-read.js", () => ({
  formatHolidayOverlays: vi.fn(),
  loadTimelineHolidayOverlaysForReadModel: vi.fn(),
  summarizeHolidayOverlays: vi.fn(),
}));

import { getAnonymizationDirectory } from "../lib/anonymization.js";
import {
  buildSelfServiceTimelineInput,
  buildTimelineEntriesDetailInput,
  createEmptyTimelineEntriesView,
  loadTimelineEntriesReadModel,
} from "../router/timeline-read-shared.js";
import {
  buildTimelineEntriesDetailResponse,
  buildTimelineEntriesViewResponse,
} from "../router/timeline-entry-response-support.js";
import {
  formatHolidayOverlays,
  loadTimelineHolidayOverlaysForReadModel,
  summarizeHolidayOverlays,
} from "../router/timeline-holiday-read.js";
import {
  readMyTimelineEntriesView,
  readTimelineEntries,
  readTimelineEntriesDetail,
  readTimelineEntriesView,
} from "../router/timeline-entry-procedure-support.js";

const getAnonymizationDirectoryMock = vi.mocked(getAnonymizationDirectory);
const buildSelfServiceTimelineInputMock = vi.mocked(buildSelfServiceTimelineInput);
const buildTimelineEntriesDetailInputMock = vi.mocked(buildTimelineEntriesDetailInput);
const createEmptyTimelineEntriesViewMock = vi.mocked(createEmptyTimelineEntriesView);
const loadTimelineEntriesReadModelMock = vi.mocked(loadTimelineEntriesReadModel);
const buildTimelineEntriesDetailResponseMock = vi.mocked(buildTimelineEntriesDetailResponse);
const buildTimelineEntriesViewResponseMock = vi.mocked(buildTimelineEntriesViewResponse);
const formatHolidayOverlaysMock = vi.mocked(formatHolidayOverlays);
const loadTimelineHolidayOverlaysForReadModelMock = vi.mocked(loadTimelineHolidayOverlaysForReadModel);
const summarizeHolidayOverlaysMock = vi.mocked(summarizeHolidayOverlays);

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

  it("returns anonymized allocations for entry list reads", async () => {
    const readModel = { allocations: [{ id: "allocation_1" }], assignments: [] };
    const directory = { enabled: true };
    const response = { allocations: [{ id: "allocation_1", anonymized: true }], assignments: [] };

    loadTimelineEntriesReadModelMock.mockResolvedValueOnce(readModel as never);
    getAnonymizationDirectoryMock.mockResolvedValueOnce(directory as never);
    buildTimelineEntriesViewResponseMock.mockReturnValueOnce(response as never);

    await expect(
      readTimelineEntries({} as never, {
        startDate: new Date("2026-04-01T00:00:00.000Z"),
        endDate: new Date("2026-04-07T00:00:00.000Z"),
      }),
    ).resolves.toEqual([{ id: "allocation_1", anonymized: true }]);

    expect(loadTimelineEntriesReadModelMock).toHaveBeenCalledWith({}, {
      startDate: new Date("2026-04-01T00:00:00.000Z"),
      endDate: new Date("2026-04-07T00:00:00.000Z"),
    });
    expect(getAnonymizationDirectoryMock).toHaveBeenCalledWith({});
    expect(buildTimelineEntriesViewResponseMock).toHaveBeenCalledWith(readModel, directory);
  });

  it("returns an empty entries view when self-service scope cannot be resolved", async () => {
    buildSelfServiceTimelineInputMock.mockResolvedValueOnce(null);
    createEmptyTimelineEntriesViewMock.mockReturnValueOnce({
      allocations: [],
      assignments: [],
      demands: [],
    } as never);

    await expect(
      readMyTimelineEntriesView(
        { db: {}, dbUser: { id: "user_1" } } as never,
        {
          startDate: new Date("2026-04-01T00:00:00.000Z"),
          endDate: new Date("2026-04-07T00:00:00.000Z"),
        },
      ),
    ).resolves.toEqual({
      allocations: [],
      assignments: [],
      demands: [],
    });

    expect(createEmptyTimelineEntriesViewMock).toHaveBeenCalledTimes(1);
    expect(loadTimelineEntriesReadModelMock).not.toHaveBeenCalled();
  });

  it("builds entry detail responses with formatted holiday overlays", async () => {
    const period = {
      startDate: new Date("2026-04-01T00:00:00.000Z"),
      endDate: new Date("2026-04-07T00:00:00.000Z"),
    };
    const filters = { countryCodes: ["DE"] };
    const timelineInput = {
      startDate: new Date("2026-04-01T00:00:00.000Z"),
      endDate: new Date("2026-04-07T00:00:00.000Z"),
      countryCodes: ["DE"],
    };
    const readModel = {
      allocations: [{ id: "allocation_1" }],
      demands: [{ id: "demand_1" }],
      assignments: [{ id: "assignment_1" }],
    };

    buildTimelineEntriesDetailInputMock.mockReturnValueOnce({
      period,
      filters,
      timelineInput,
    } as never);
    loadTimelineEntriesReadModelMock.mockResolvedValueOnce(readModel as never);
    getAnonymizationDirectoryMock.mockResolvedValueOnce({ enabled: true } as never);
    loadTimelineHolidayOverlaysForReadModelMock.mockResolvedValueOnce([{ id: "overlay_1" }] as never);
    formatHolidayOverlaysMock.mockReturnValueOnce([{ id: "formatted_1" }] as never);
    summarizeHolidayOverlaysMock.mockReturnValueOnce({
      overlayCount: 1,
      holidayResourceCount: 1,
      byScope: [{ scope: "COUNTRY", count: 1 }],
    } as never);
    buildTimelineEntriesDetailResponseMock.mockReturnValueOnce({ detail: true } as never);

    await expect(
      readTimelineEntriesDetail({} as never, {
        startDate: "2026-04-01",
        endDate: "2026-04-07",
        countryCodes: ["DE"],
      }),
    ).resolves.toEqual({ detail: true });

    expect(loadTimelineHolidayOverlaysForReadModelMock).toHaveBeenCalledWith(
      {},
      timelineInput,
      readModel,
    );
    expect(formatHolidayOverlaysMock).toHaveBeenCalledWith([{ id: "overlay_1" }]);
    expect(summarizeHolidayOverlaysMock).toHaveBeenCalledWith([{ id: "formatted_1" }]);
    expect(buildTimelineEntriesDetailResponseMock).toHaveBeenCalledWith({
      period,
      filters,
      readModel,
      directory: { enabled: true },
      holidayOverlays: [{ id: "formatted_1" }],
      holidaySummary: {
        overlayCount: 1,
        holidayResourceCount: 1,
        byScope: [{ scope: "COUNTRY", count: 1 }],
      },
    });
  });

  it("builds full entries views from loaded read models", async () => {
    const readModel = { allocations: [{ id: "allocation_1" }], assignments: [{ id: "assignment_1" }] };
    const directory = { enabled: true };
    const response = {
      allocations: [{ id: "allocation_1", anonymized: true }],
      assignments: [{ id: "assignment_1", anonymized: true }],
    };

    loadTimelineEntriesReadModelMock.mockResolvedValueOnce(readModel as never);
    getAnonymizationDirectoryMock.mockResolvedValueOnce(directory as never);
    buildTimelineEntriesViewResponseMock.mockReturnValueOnce(response as never);

    await expect(
      readTimelineEntriesView({} as never, {
        startDate: new Date("2026-04-01T00:00:00.000Z"),
        endDate: new Date("2026-04-07T00:00:00.000Z"),
      }),
    ).resolves.toEqual(response);
  });
});
