import React from 'react';
import { renderHook } from '@testing-library/react-native';
import { useDragGesture } from '../../hooks/drag/useDragGesture';
import { DragStateProvider } from '../../contexts/DragStateContext';
import { createMockAnimatedRef } from '../utils/test-utils';

// Mock scheduleOnRN to execute synchronously in tests
jest.mock('react-native-worklets', () => ({
  scheduleOnRN: jest.fn((fn, ...args) => fn(...args)),
}));

describe('useDragGesture', () => {
  const createWrapper = () => {
    const Wrapper = ({ children }: { children: React.ReactNode }) => (
      <DragStateProvider>{children}</DragStateProvider>
    );
    return Wrapper;
  };

  const defaultConfig = {
    itemId: 'item-1',
    index: 0,
    totalItems: 5,
    enabled: true,
    containerRef: createMockAnimatedRef(),
  };

  const defaultCallbacks = {
    onReorderByDelta: jest.fn(),
    onHapticFeedback: jest.fn(),
  };

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('returns panGesture, isDragging, and translateY', async () => {
    const { result } = await renderHook(
      () => useDragGesture(defaultConfig, defaultCallbacks),
      { wrapper: createWrapper() },
    );

    expect(result.current.panGesture).toBeDefined();
    expect(result.current.isDragging).toBeDefined();
    expect(result.current.translateY).toBeDefined();
  });

  it('initializes isDragging to false', async () => {
    const { result } = await renderHook(
      () => useDragGesture(defaultConfig, defaultCallbacks),
      { wrapper: createWrapper() },
    );

    expect(result.current.isDragging.value).toBe(false);
  });

  it('initializes translateY to 0', async () => {
    const { result } = await renderHook(
      () => useDragGesture(defaultConfig, defaultCallbacks),
      { wrapper: createWrapper() },
    );

    expect(result.current.translateY.value).toBe(0);
  });

  it('creates a Pan gesture with long press activation', async () => {
    const { result } = await renderHook(
      () => useDragGesture(defaultConfig, defaultCallbacks),
      { wrapper: createWrapper() },
    );

    // Gesture should be created by Gesture.Pan()
    expect(result.current.panGesture).toBeDefined();
  });

  it('respects the enabled config', async () => {
    const { result } = await renderHook(
      () =>
        useDragGesture(
          { ...defaultConfig, enabled: false },
          defaultCallbacks,
        ),
      { wrapper: createWrapper() },
    );

    // Gesture is still created but enabled is set via config
    expect(result.current.panGesture).toBeDefined();
  });

  it('uses the containerRef for measuring', async () => {
    const containerRef = createMockAnimatedRef();

    const { result } = await renderHook(
      () =>
        useDragGesture(
          { ...defaultConfig, containerRef },
          defaultCallbacks,
        ),
      { wrapper: createWrapper() },
    );

    expect(result.current.panGesture).toBeDefined();
  });

  it('updates gesture when config changes', async () => {
    const { result, rerender } = await renderHook(
      ({ config }) => useDragGesture(config, defaultCallbacks),
      {
        wrapper: createWrapper(),
        initialProps: { config: defaultConfig },
      },
    );

    const initialGesture = result.current.panGesture;

    await rerender({
      config: { ...defaultConfig, enabled: false },
    });

    // Gesture should be recreated when enabled changes
    expect(result.current.panGesture).toBeDefined();
  });

  it('accepts different item indices', async () => {
    // Test with different indices sequentially to avoid overlapping act() calls
    for (const index of [0, 2, 4]) {
      const { result } = await renderHook(
        () =>
          useDragGesture(
            { ...defaultConfig, index, itemId: `item-${index}` },
            defaultCallbacks,
          ),
        { wrapper: createWrapper() },
      );

      expect(result.current.panGesture).toBeDefined();
      expect(result.current.isDragging.value).toBe(false);
      expect(result.current.translateY.value).toBe(0);
    }
  });

  it('handles haptic feedback callback', async () => {
    const onHapticFeedback = jest.fn();

    const { result } = await renderHook(
      () => useDragGesture(defaultConfig, { ...defaultCallbacks, onHapticFeedback }),
      { wrapper: createWrapper() },
    );

    // Callback should be storable and gesture should be created
    expect(result.current.panGesture).toBeDefined();
  });

  it('handles reorder callback', async () => {
    const onReorderByDelta = jest.fn();

    const { result } = await renderHook(
      () =>
        useDragGesture(defaultConfig, { ...defaultCallbacks, onReorderByDelta }),
      { wrapper: createWrapper() },
    );

    // Callback should be storable and gesture should be created
    expect(result.current.panGesture).toBeDefined();
  });

});
