import type { TouchEvent } from "react";
import { describe, expect, it, vi } from "vitest";
import {
  forwardCanvasTouchEnd,
  forwardCanvasTouchMove,
  forwardTouchStartAsMouseDown,
} from "./timelineTouchEvents.js";

function createTouchEvent<TCurrentTarget>(
  target: TCurrentTarget,
  point: { clientX: number; clientY: number },
) {
  return {
    currentTarget: target,
    preventDefault: vi.fn(),
    touches: [point],
    changedTouches: [point],
  } as unknown as TouchEvent<TCurrentTarget>;
}

describe("timelineTouchEvents", () => {
  it("forwards touch starts as mouse-down events and marks the decision state", () => {
    const onMouseDown = vi.fn();
    const touchStartRef = { current: { x: 0, y: 0, decided: false } };
    const event = createTouchEvent({ id: "row" }, { clientX: 18, clientY: 42 });

    forwardTouchStartAsMouseDown({
      event,
      touchStartRef,
      decided: true,
      onMouseDown,
      opts: { allocationId: "alloc-1" },
    });

    expect(event.preventDefault).toHaveBeenCalledOnce();
    expect(touchStartRef.current).toEqual({ x: 18, y: 42, decided: true });
    expect(onMouseDown).toHaveBeenCalledWith(
      expect.objectContaining({ clientX: 18, button: 0, currentTarget: { id: "row" } }),
      { allocationId: "alloc-1" },
    );
  });

  it("keeps touch move in scroll mode until drag handling is approved", () => {
    const onCanvasMouseMove = vi.fn();
    const touchStartRef = { current: { x: 10, y: 10, decided: false } };

    forwardCanvasTouchMove({
      event: createTouchEvent({ id: "canvas" }, { clientX: 11, clientY: 28 }),
      touchStartRef,
      onCanvasMouseMove,
    });

    expect(onCanvasMouseMove).not.toHaveBeenCalled();
    expect(touchStartRef.current).toEqual({ x: 10, y: 10, decided: true });
  });

  it("forwards touch move once the touch policy resolves to drag handling", () => {
    const onCanvasMouseMove = vi.fn();
    const touchStartRef = { current: { x: 10, y: 10, decided: false } };

    forwardCanvasTouchMove({
      event: createTouchEvent({ id: "canvas" }, { clientX: 40, clientY: 12 }),
      touchStartRef,
      onCanvasMouseMove,
    });

    expect(onCanvasMouseMove).toHaveBeenCalledWith(expect.objectContaining({ clientX: 40, clientY: 12 }));
    expect(touchStartRef.current).toEqual({ x: 10, y: 10, decided: true });
  });

  it("awaits touch-end forwarding through the canvas mouse-up path", async () => {
    const onCanvasMouseUp = vi.fn().mockResolvedValue(undefined);

    await forwardCanvasTouchEnd({
      event: createTouchEvent({ id: "canvas" }, { clientX: 55, clientY: 21 }),
      onCanvasMouseUp,
    });

    expect(onCanvasMouseUp).toHaveBeenCalledWith(expect.objectContaining({ clientX: 55, clientY: 21 }));
  });
});
