import { describe, expect, it, vi } from "vitest";
import { cancelTransientMultiSelectState, cleanupTimelineDragState } from "./timelineDragCleanup.js";

type MultiSelectStateFixture = {
  isSelecting: boolean;
  startX: number;
  startY: number;
  currentX: number;
  currentY: number;
  selectedAllocationIds: string[];
  selectedResourceIds: string[];
  dateRange: { start: Date; end: Date } | null;
  multiDragDaysDelta: number;
  isMultiDragging: boolean;
  multiDragMode: string;
};

describe("timelineDragCleanup", () => {
  it("runs registered cleanup callbacks, clears previews, and resets refs", () => {
    const projectCleanup = vi.fn();
    const allocCleanup = vi.fn();
    const multiCleanup = vi.fn();
    const projectPreview = { id: "project-preview" };
    const allocPreview = { id: "alloc-preview" };
    const clearPreview = vi.fn();

    const projectDragCleanupRef = { current: projectCleanup };
    const allocDragCleanupRef = { current: allocCleanup };
    const multiSelectCleanupRef = { current: multiCleanup };
    const projectPreviewRef = { current: projectPreview };
    const allocPreviewRef = { current: allocPreview };
    const dragStateRef = { current: { drag: true } };
    const allocDragRef = { current: { alloc: true } };
    const rangeStateRef = { current: { range: true } };
    const multiSelectRef = { current: { multi: true } };

    const initialDragState = { drag: false };
    const initialAllocDragState = { alloc: false };
    const initialRangeState = { range: false };
    const initialMultiSelectState = { multi: false };

    cleanupTimelineDragState({
      projectDragCleanupRef,
      allocDragCleanupRef,
      multiSelectCleanupRef,
      projectPreviewRef,
      allocPreviewRef,
      dragStateRef,
      allocDragRef,
      rangeStateRef,
      multiSelectRef,
      initialDragState,
      initialAllocDragState,
      initialRangeState,
      initialMultiSelectState,
      clearPreview,
    });

    expect(projectCleanup).toHaveBeenCalledOnce();
    expect(allocCleanup).toHaveBeenCalledOnce();
    expect(multiCleanup).toHaveBeenCalledOnce();
    expect(clearPreview).toHaveBeenNthCalledWith(1, projectPreview);
    expect(clearPreview).toHaveBeenNthCalledWith(2, allocPreview);
    expect(projectDragCleanupRef.current).toBeNull();
    expect(allocDragCleanupRef.current).toBeNull();
    expect(multiSelectCleanupRef.current).toBeNull();
    expect(projectPreviewRef.current).toBeNull();
    expect(allocPreviewRef.current).toBeNull();
    expect(dragStateRef.current).toBe(initialDragState);
    expect(allocDragRef.current).toBe(initialAllocDragState);
    expect(rangeStateRef.current).toBe(initialRangeState);
    expect(multiSelectRef.current).toBe(initialMultiSelectState);
  });

  it("tolerates missing cleanups and preview sessions", () => {
    const clearPreview = vi.fn();

    cleanupTimelineDragState({
      projectDragCleanupRef: { current: null },
      allocDragCleanupRef: { current: null },
      multiSelectCleanupRef: { current: null },
      projectPreviewRef: { current: null },
      allocPreviewRef: { current: null },
      dragStateRef: { current: { drag: true } },
      allocDragRef: { current: { alloc: true } },
      rangeStateRef: { current: { range: true } },
      multiSelectRef: { current: { multi: true } },
      initialDragState: { drag: false },
      initialAllocDragState: { alloc: false },
      initialRangeState: { range: false },
      initialMultiSelectState: { multi: false },
      clearPreview,
    });

    expect(clearPreview).toHaveBeenCalledTimes(2);
    expect(clearPreview).toHaveBeenNthCalledWith(1, null);
    expect(clearPreview).toHaveBeenNthCalledWith(2, null);
  });

  it("keeps completed multi-selection while cancelling transient drag state", () => {
    const initialState: MultiSelectStateFixture = {
      isSelecting: false,
      startX: 0,
      startY: 0,
      currentX: 0,
      currentY: 0,
      selectedAllocationIds: [],
      selectedResourceIds: [],
      dateRange: null,
      multiDragDaysDelta: 0,
      isMultiDragging: false,
      multiDragMode: "move",
    };

    const nextState = cancelTransientMultiSelectState(
      {
        isSelecting: false,
        startX: 140,
        startY: 32,
        currentX: 244,
        currentY: 96,
        selectedAllocationIds: ["alloc-1", "alloc-2"],
        selectedResourceIds: ["res-1"],
        dateRange: { start: new Date("2026-04-02"), end: new Date("2026-04-05") },
        multiDragDaysDelta: 3,
        isMultiDragging: true,
        multiDragMode: "resize-end",
      },
      initialState,
    );

    expect(nextState).toEqual({
      isSelecting: false,
      startX: 0,
      startY: 0,
      currentX: 0,
      currentY: 0,
      selectedAllocationIds: ["alloc-1", "alloc-2"],
      selectedResourceIds: ["res-1"],
      dateRange: { start: new Date("2026-04-02"), end: new Date("2026-04-05") },
      multiDragDaysDelta: 0,
      isMultiDragging: false,
      multiDragMode: "move",
    });
  });

  it("drops incomplete marquee state when nothing was selected yet", () => {
    const initialState: MultiSelectStateFixture = {
      isSelecting: false,
      startX: 0,
      startY: 0,
      currentX: 0,
      currentY: 0,
      selectedAllocationIds: [],
      selectedResourceIds: [],
      dateRange: null,
      multiDragDaysDelta: 0,
      isMultiDragging: false,
      multiDragMode: "move",
    };

    const nextState = cancelTransientMultiSelectState(
      {
        ...initialState,
        isSelecting: true,
        startX: 180,
        startY: 24,
        currentX: 220,
        currentY: 68,
      },
      initialState,
    );

    expect(nextState).toBe(initialState);
  });
});
