import { describe, it, expect } from 'vitest';
import { createStore } from 'jotai';
import {
    activePointersAtom,
    primaryInputSourceAtom,
    inputCapabilitiesAtom,
    isStylusActiveAtom,
    isMultiTouchAtom,
    fingerCountAtom,
    isTouchDeviceAtom,
    pointerDownAtom,
    pointerUpAtom,
    clearPointersAtom,
} from '../core/input-store';
import type { ClassifiedPointer } from '../core/input-classifier';

function createPointer(overrides: Partial<ClassifiedPointer> = {}): ClassifiedPointer {
    return {
        source: 'mouse',
        pointerId: 1,
        pressure: 0.5,
        tiltX: 0,
        tiltY: 0,
        isPrimary: true,
        rawPointerType: 'mouse',
        ...overrides,
    };
}

// =============================================================================
// Pointer tracking
// =============================================================================

describe('input-store', () => {
    describe('pointer tracking', () => {
        it('tracks pointer down/up', () => {
            const store = createStore();
            expect(store.get(activePointersAtom).size).toBe(0);

            const pointer = createPointer({ pointerId: 1 });
            store.set(pointerDownAtom, pointer);
            expect(store.get(activePointersAtom).size).toBe(1);

            store.set(pointerUpAtom, 1);
            expect(store.get(activePointersAtom).size).toBe(0);
        });

        it('handles multiple concurrent pointers', () => {
            const store = createStore();

            store.set(pointerDownAtom, createPointer({ pointerId: 1, source: 'finger' }));
            store.set(pointerDownAtom, createPointer({ pointerId: 2, source: 'finger' }));
            expect(store.get(activePointersAtom).size).toBe(2);

            store.set(pointerUpAtom, 1);
            expect(store.get(activePointersAtom).size).toBe(1);
        });

        it('clearPointers removes all', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ pointerId: 1 }));
            store.set(pointerDownAtom, createPointer({ pointerId: 2 }));
            expect(store.get(activePointersAtom).size).toBe(2);

            store.set(clearPointersAtom);
            expect(store.get(activePointersAtom).size).toBe(0);
        });
    });

    // =============================================================================
    // Primary input source
    // =============================================================================

    describe('primaryInputSourceAtom', () => {
        it('defaults to mouse', () => {
            const store = createStore();
            expect(store.get(primaryInputSourceAtom)).toBe('mouse');
        });

        it('updates to last pointer source', () => {
            const store = createStore();

            store.set(pointerDownAtom, createPointer({ source: 'finger', rawPointerType: 'touch' }));
            expect(store.get(primaryInputSourceAtom)).toBe('finger');

            store.set(pointerDownAtom, createPointer({ pointerId: 2, source: 'pencil', rawPointerType: 'pen' }));
            expect(store.get(primaryInputSourceAtom)).toBe('pencil');
        });
    });

    // =============================================================================
    // Derived atoms
    // =============================================================================

    describe('isStylusActiveAtom', () => {
        it('false when no pencil pointer', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ source: 'finger' }));
            expect(store.get(isStylusActiveAtom)).toBe(false);
        });

        it('true when pencil pointer is down', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ source: 'pencil', rawPointerType: 'pen' }));
            expect(store.get(isStylusActiveAtom)).toBe(true);
        });

        it('false after pencil pointer is lifted', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ pointerId: 5, source: 'pencil', rawPointerType: 'pen' }));
            expect(store.get(isStylusActiveAtom)).toBe(true);

            store.set(pointerUpAtom, 5);
            expect(store.get(isStylusActiveAtom)).toBe(false);
        });
    });

    describe('isMultiTouchAtom', () => {
        it('false with one finger', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ pointerId: 1, source: 'finger' }));
            expect(store.get(isMultiTouchAtom)).toBe(false);
        });

        it('true with two fingers', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ pointerId: 1, source: 'finger' }));
            store.set(pointerDownAtom, createPointer({ pointerId: 2, source: 'finger' }));
            expect(store.get(isMultiTouchAtom)).toBe(true);
        });

        it('false with one finger and one pencil', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ pointerId: 1, source: 'finger' }));
            store.set(pointerDownAtom, createPointer({ pointerId: 2, source: 'pencil' }));
            expect(store.get(isMultiTouchAtom)).toBe(false);
        });
    });

    describe('fingerCountAtom', () => {
        it('counts only finger pointers', () => {
            const store = createStore();
            store.set(pointerDownAtom, createPointer({ pointerId: 1, source: 'finger' }));
            store.set(pointerDownAtom, createPointer({ pointerId: 2, source: 'pencil' }));
            store.set(pointerDownAtom, createPointer({ pointerId: 3, source: 'finger' }));
            expect(store.get(fingerCountAtom)).toBe(2);
        });
    });

    describe('inputCapabilitiesAtom', () => {
        it('auto-detects stylus on first pen event', () => {
            const store = createStore();
            expect(store.get(inputCapabilitiesAtom).hasStylus).toBe(false);

            store.set(pointerDownAtom, createPointer({ source: 'pencil', rawPointerType: 'pen' }));
            expect(store.get(inputCapabilitiesAtom).hasStylus).toBe(true);
        });
    });
});
