// @vitest-environment jsdom

import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { SwipeToArchive } from "./SwipeToArchive";

// Tell React this environment uses act() for event flushing.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;

function dispatchTouchEvent(
  node: Element,
  type: "touchstart" | "touchmove" | "touchend",
  coords: { x: number; y: number },
) {
  const event = new Event(type, { bubbles: true, cancelable: true });
  const touchPoint = { clientX: coords.x, clientY: coords.y };

  Object.defineProperty(event, "touches", {
    configurable: true,
    value: type === "touchend" ? [] : [touchPoint],
  });
  Object.defineProperty(event, "changedTouches", {
    configurable: true,
    value: [touchPoint],
  });

  node.dispatchEvent(event);
}

describe("SwipeToArchive", () => {
  let container: HTMLDivElement;

  beforeEach(() => {
    container = document.createElement("div");
    document.body.appendChild(container);
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.runOnlyPendingTimers();
    vi.useRealTimers();
    container.remove();
  });

  it("suppresses descendant clicks after a horizontal swipe and archives the row", () => {
    const onArchive = vi.fn();
    const onClick = vi.fn();
    const root = createRoot(container);

    act(() => {
      root.render(
        <SwipeToArchive onArchive={onArchive}>
          <button type="button" onClick={onClick}>
            Open issue
          </button>
        </SwipeToArchive>,
      );
    });

    const wrapper = container.firstElementChild as HTMLDivElement;
    const button = container.querySelector("button");
    expect(button).not.toBeNull();

    Object.defineProperty(wrapper, "offsetWidth", { configurable: true, value: 200 });
    Object.defineProperty(wrapper, "offsetHeight", { configurable: true, value: 48 });

    act(() => {
      dispatchTouchEvent(wrapper, "touchstart", { x: 180, y: 20 });
    });
    act(() => {
      dispatchTouchEvent(wrapper, "touchmove", { x: 80, y: 22 });
    });
    act(() => {
      dispatchTouchEvent(wrapper, "touchend", { x: 80, y: 22 });
    });

    act(() => {
      button!.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
    });

    expect(onClick).not.toHaveBeenCalled();

    act(() => {
      vi.advanceTimersByTime(140);
    });

    expect(onArchive).toHaveBeenCalledTimes(1);

    act(() => {
      root.unmount();
    });
  });

  it("does not suppress a normal tap click", () => {
    const onArchive = vi.fn();
    const onClick = vi.fn();
    const root = createRoot(container);

    act(() => {
      root.render(
        <SwipeToArchive onArchive={onArchive}>
          <button type="button" onClick={onClick}>
            Open issue
          </button>
        </SwipeToArchive>,
      );
    });

    const button = container.querySelector("button");
    expect(button).not.toBeNull();

    act(() => {
      button!.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
    });

    expect(onClick).toHaveBeenCalledTimes(1);
    expect(onArchive).not.toHaveBeenCalled();

    act(() => {
      root.unmount();
    });
  });

  it("renders the selected inbox treatment on the swipe surface", () => {
    const root = createRoot(container);

    act(() => {
      root.render(
        <SwipeToArchive onArchive={() => {}} selected>
          <button type="button">Open issue</button>
        </SwipeToArchive>,
      );
    });

    const surface = container.querySelector("[data-inbox-row-surface]") as HTMLDivElement | null;
    expect(surface).not.toBeNull();
    expect(surface?.style.backgroundColor).toBe("hsl(var(--muted))");
    expect(surface?.style.boxShadow).toBe("");

    act(() => {
      root.unmount();
    });
  });
});
