import { describe, expect, it } from "vitest";
import { createAllocationPreviewSession, createProjectPreviewSession } from "./timelinePreviewSession.js";

function createFakeElement({
  left = "0px",
  width = "0px",
  transform = "",
  closest = () => null,
}: {
  left?: string;
  width?: string;
  transform?: string;
  closest?: () => HTMLElement | null;
} = {}): HTMLElement {
  return {
    style: { left, width, transform },
    closest,
  } as unknown as HTMLElement;
}

function isFakeElement(value: EventTarget | null | undefined): value is HTMLElement {
  return Boolean(value && typeof value === "object" && "style" in value);
}

describe("timelinePreviewSession", () => {
  it("returns null when project preview lookup finds nothing and no HTMLElement fallback exists", () => {
    const session = createProjectPreviewSession({
      projectId: "proj_1",
      currentTarget: null,
      cellWidth: 32,
      queryProjectTargets: () => [],
      isElement: isFakeElement,
    });

    expect(session).toBeNull();
  });

  it("falls back to the current target when project preview lookup returns no matching elements", () => {
    const currentTarget = createFakeElement({ left: "12px", width: "48px" });

    const session = createProjectPreviewSession({
      projectId: "proj_1",
      currentTarget,
      cellWidth: 32,
      queryProjectTargets: () => [],
      isElement: isFakeElement,
    });

    expect(session).toMatchObject({
      mode: "move",
      cellWidth: 32,
      pointerDeltaX: 0,
      daysDelta: 0,
      frame: null,
    });
    expect(session?.targets).toHaveLength(1);
    expect(session?.targets[0]).toMatchObject({
      element: currentTarget,
      baseLeft: 12,
      baseWidth: 48,
    });
  });

  it("keeps queried project preview targets authoritative when they exist", () => {
    const queriedTarget = createFakeElement({ left: "20px", width: "64px" });
    const fallbackTarget = createFakeElement({ left: "99px", width: "99px" });

    const session = createProjectPreviewSession({
      projectId: "proj_1",
      currentTarget: fallbackTarget,
      cellWidth: 16,
      queryProjectTargets: () => [queriedTarget],
      isElement: isFakeElement,
    });

    expect(session?.targets).toHaveLength(1);
    expect(session?.targets[0]).toMatchObject({
      element: queriedTarget,
      baseLeft: 20,
      baseWidth: 64,
    });
  });

  it("returns null when allocation preview root cannot be resolved", () => {
    const currentTarget = createFakeElement();

    expect(
      createAllocationPreviewSession({
        currentTarget,
        cellWidth: 24,
        resolveAllocationRoot: () => null,
      }),
    ).toBeNull();
  });

  it("captures the nearest allocation preview root for allocation sessions", () => {
    const root = createFakeElement({ left: "8px", width: "40px" });
    const child = createFakeElement();

    const session = createAllocationPreviewSession({
      currentTarget: child,
      mode: "resize-end",
      cellWidth: 24,
      resolveAllocationRoot: () => root,
    });

    expect(session).toMatchObject({
      mode: "resize-end",
      cellWidth: 24,
      pointerDeltaX: 0,
      daysDelta: 0,
      frame: null,
    });
    expect(session?.targets).toHaveLength(1);
    expect(session?.targets[0]).toMatchObject({
      element: root,
      baseLeft: 8,
      baseWidth: 40,
    });
  });
});
