import { describe, expect, it, vi } from "vitest";
import {
  buildDemandHoverData,
  findVacationHit,
  scheduleVacationHoverUpdate,
} from "./timelineHover.js";

describe("timelineHover", () => {
  it("matches vacation hits inclusively across differing time components", () => {
    const hit = findVacationHit(
      [
        {
          id: "vacation_1",
          startDate: "2026-04-10T15:00:00.000Z",
          endDate: "2026-04-12T01:00:00.000Z",
        },
      ],
      new Date("2026-04-12T23:59:59.000Z"),
    );

    expect(hit?.id).toBe("vacation_1");
  });

  it("does not schedule a second hover frame while one is already pending", () => {
    const frameRef = { current: 42 };
    const hoveredKeyRef = { current: null };
    const onHoverChange = vi.fn();
    const requestAnimationFrameMock = vi.fn();

    vi.stubGlobal("requestAnimationFrame", requestAnimationFrameMock);

    scheduleVacationHoverUpdate({
      frameRef,
      hoveredKeyRef,
      resourceId: "resource_1",
      clientX: 160,
      rect: {} as DOMRect,
      xToDate: () => new Date("2026-04-10T00:00:00.000Z"),
      vacations: [{ id: "vacation_1", startDate: "2026-04-10", endDate: "2026-04-12" }],
      onHoverChange,
    });

    expect(requestAnimationFrameMock).not.toHaveBeenCalled();
    expect(onHoverChange).not.toHaveBeenCalled();
  });

  it("suppresses duplicate hover notifications when the hovered vacation does not change", () => {
    const scheduledFrames: Array<(timestamp: number) => void> = [];

    vi.stubGlobal("requestAnimationFrame", vi.fn((callback: (timestamp: number) => void) => {
      scheduledFrames.push(callback);
      return 7;
    }));

    const frameRef = { current: null as number | null };
    const hoveredKeyRef = { current: "resource_1:vacation_1" };
    const onHoverChange = vi.fn();

    scheduleVacationHoverUpdate({
      frameRef,
      hoveredKeyRef,
      resourceId: "resource_1",
      clientX: 160,
      rect: {} as DOMRect,
      xToDate: () => new Date("2026-04-11T12:00:00.000Z"),
      vacations: [{ id: "vacation_1", startDate: "2026-04-10", endDate: "2026-04-12" }],
      onHoverChange,
    });

    expect(frameRef.current).toBe(7);
    expect(scheduledFrames).toHaveLength(1);
    scheduledFrames[0]!(0);

    expect(frameRef.current).toBeNull();
    expect(onHoverChange).not.toHaveBeenCalled();
    expect(hoveredKeyRef.current).toBe("resource_1:vacation_1");
  });

  it("omits cost data and clamps total hours to at least one day for inverted demand ranges", () => {
    const hoverData = buildDemandHoverData({
      roleEntity: null,
      role: null,
      project: {
        name: "Project Atlas",
        shortCode: "ATL",
      },
      requestedHeadcount: 2,
      unfilledHeadcount: 1,
      startDate: "2026-04-14",
      endDate: "2026-04-13",
      hoursPerDay: 6,
      percentage: 50,
      status: "open",
      dailyCostCents: 0,
    } as never);

    expect(hoverData.roleName).toBe("Open demand");
    expect(hoverData.totalHours).toBe(6);
    expect(hoverData).not.toHaveProperty("dailyCostCents");
    expect(hoverData).not.toHaveProperty("totalCostCents");
  });
});
