import fc from "fast-check";
import { describe, expect, it } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import React from "react";
import { spy } from "tinyspy";
import { useSubscribe } from "../../rx/hook";
import { uuid } from "../../utils";
import { useDrag } from "./useDrag";
const ROLE = "separator";
let Test = ({ onStart = () => { }, onDrag = () => { }, id }) => {
    let { onPointerDown, dragStart$, drag$ } = useDrag();
    useSubscribe(dragStart$, onStart);
    useSubscribe(drag$, onDrag);
    return <div data-testid={id} role={ROLE} onPointerDown={onPointerDown}/>;
};
describe("hook:useDrag", () => {
    it("handles drag start", () => {
        let id = uuid();
        let onStart = spy();
        let onDrag = spy();
        render(<Test id={id} onStart={onStart} onDrag={onDrag}/>);
        fireEvent.pointerDown(screen.getByTestId(id));
        expect(onStart.callCount).toBe(1);
        expect(onDrag.callCount).toBe(0);
    });
    it("handles drag move", () => {
        let id = uuid();
        let onStart = spy();
        let onDrag = spy();
        render(<Test id={id} onStart={onStart} onDrag={onDrag}/>);
        fireEvent.pointerDown(screen.getByTestId(id));
        fireEvent.pointerMove(document, { clientX: 10, clientY: 10 });
        expect(onStart.callCount).toBe(1);
        expect(onDrag.callCount).toBe(1);
    });
    it("handles drag end (blur)", () => {
        let id = uuid();
        render(<Test id={id}/>);
        const element = screen.getByTestId(id);
        element.blur = spy();
        fireEvent.pointerDown(element);
        fireEvent.pointerMove(document, { clientX: 10, clientY: 10 });
        fireEvent.pointerUp(document);
        expect(element.blur.callCount).toBe(1);
    });
    it("ignores move without start", () => {
        let id = uuid();
        let onStart = spy();
        let onDrag = spy();
        render(<Test id={id} onStart={onStart} onDrag={onDrag}/>);
        fireEvent.pointerMove(document, { clientX: 10, clientY: 10 });
        expect(onStart.callCount).toBe(0);
        expect(onDrag.callCount).toBe(0);
    });
    it("cancels drag on Escape key", () => {
        let id = uuid();
        let onStart = spy();
        let onDrag = spy();
        render(<Test id={id} onStart={onStart} onDrag={onDrag}/>);
        const element = screen.getByTestId(id);
        element.blur = spy();
        fireEvent.pointerDown(element);
        fireEvent.pointerMove(document, { clientX: 10, clientY: 10 });
        expect(onStart.callCount).toBe(1);
        expect(onDrag.callCount).toBe(1);
        fireEvent.keyUp(document, { key: "Escape" });
        expect(element.blur.callCount).toBe(1);
        // Should not emit more drag events
        fireEvent.pointerMove(document, { clientX: 20, clientY: 20 });
        expect(onDrag.callCount).toBe(1);
    });
    it("fuzzing: handles random pointer movements", () => {
        fc.assert(fc.property(fc.array(fc.record({
            clientX: fc.integer({ min: 0, max: 1000 }),
            clientY: fc.integer({ min: 0, max: 1000 })
        }), { minLength: 1 }), (moves) => {
            let id = uuid();
            let onDrag = spy();
            render(<Test id={id} onDrag={onDrag}/>);
            let element = screen.getByTestId(id);
            // Mock offsetWidth/Height
            Object.defineProperty(element, 'offsetWidth', { configurable: true, value: 100 });
            Object.defineProperty(element, 'offsetHeight', { configurable: true, value: 100 });
            element.blur = () => { };
            fireEvent.pointerDown(element);
            for (let move of moves) {
                fireEvent.pointerMove(document, move);
            }
            fireEvent.pointerUp(document);
            expect(onDrag.callCount).toBe(moves.length);
        }));
    });
});
