import React from 'react';
import { Text, View } from 'react-native';
import { render } from '../utils/test-utils';
import { AnimatedFlashListItem } from '../../AnimatedFlashListItem';
import type { AnimatedRenderItemInfo } from '../../types';

interface TestItem {
  id: string;
  title: string;
}

describe('AnimatedFlashListItem', () => {
  const defaultItem: TestItem = { id: 'item-1', title: 'Test Item' };

  const defaultRenderItem = ({ item }: AnimatedRenderItemInfo<TestItem>) => (
    <View testID={`content-${item.id}`}>
      <Text>{item.title}</Text>
    </View>
  );

  it('renders without crashing', async () => {
    await expect(
      render(
        <AnimatedFlashListItem<TestItem>
          item={defaultItem}
          index={0}
          totalItems={5}
          isDragEnabled={false}
          renderItem={defaultRenderItem}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('renders the item content from renderItem prop', async () => {
    const { getByText } = await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={defaultRenderItem}
      />,
    );

    expect(getByText('Test Item')).toBeTruthy();
  });

  it('passes item data to renderItem', async () => {
    let receivedItem: TestItem | null = null;

    const renderItem = ({ item }: AnimatedRenderItemInfo<TestItem>) => {
      receivedItem = item;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(receivedItem).toEqual(defaultItem);
  });

  it('passes index to renderItem', async () => {
    let receivedIndex: number | null = null;

    const renderItem = ({ item, index }: AnimatedRenderItemInfo<TestItem>) => {
      receivedIndex = index;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={3}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(receivedIndex).toBe(3);
  });

  it('passes totalItems to renderItem', async () => {
    let receivedTotalItems: number | null = null;

    const renderItem = ({ item, totalItems }: AnimatedRenderItemInfo<TestItem>) => {
      receivedTotalItems = totalItems;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={10}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(receivedTotalItems).toBe(10);
  });

  it('provides dragHandleProps when isDragEnabled is true', async () => {
    let receivedDragHandleProps: unknown = null;

    const renderItem = ({ item, dragHandleProps }: AnimatedRenderItemInfo<TestItem>) => {
      receivedDragHandleProps = dragHandleProps;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={true}
        renderItem={renderItem}
      />,
    );

    expect(receivedDragHandleProps).not.toBeNull();
    expect((receivedDragHandleProps as any).gesture).toBeDefined();
    expect((receivedDragHandleProps as any).isDragging).toBeDefined();
  });

  it('provides null dragHandleProps when isDragEnabled is false', async () => {
    let receivedDragHandleProps: unknown = 'initial';

    const renderItem = ({ item, dragHandleProps }: AnimatedRenderItemInfo<TestItem>) => {
      receivedDragHandleProps = dragHandleProps;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(receivedDragHandleProps).toBeNull();
  });

  it('provides triggerExitAnimation function', async () => {
    let receivedTriggerExit: unknown = null;

    const renderItem = ({
      item,
      triggerExitAnimation,
    }: AnimatedRenderItemInfo<TestItem>) => {
      receivedTriggerExit = triggerExitAnimation;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(typeof receivedTriggerExit).toBe('function');
  });

  it('provides resetExitAnimation function', async () => {
    let receivedResetExit: unknown = null;

    const renderItem = ({
      item,
      resetExitAnimation,
    }: AnimatedRenderItemInfo<TestItem>) => {
      receivedResetExit = resetExitAnimation;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(typeof receivedResetExit).toBe('function');
  });

  it('calls onReorderByDelta when provided', async () => {
    const onReorderByDelta = jest.fn();

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={true}
        renderItem={defaultRenderItem}
        onReorderByDelta={onReorderByDelta}
      />,
    );

    // Callback should be provided but not called until drag completes
    expect(onReorderByDelta).not.toHaveBeenCalled();
  });

  it('calls onHapticFeedback when provided', async () => {
    const onHapticFeedback = jest.fn();

    await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={true}
        renderItem={defaultRenderItem}
        onHapticFeedback={onHapticFeedback}
      />,
    );

    // Callback should be provided but not called until drag starts
    expect(onHapticFeedback).not.toHaveBeenCalled();
  });

  it('re-renders when item changes', async () => {
    const item1: TestItem = { id: 'item-1', title: 'First Item' };
    const item2: TestItem = { id: 'item-2', title: 'Second Item' };

    const { rerender, getByText, queryByText } = await render(
      <AnimatedFlashListItem<TestItem>
        item={item1}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={defaultRenderItem}
      />,
    );

    expect(getByText('First Item')).toBeTruthy();
    expect(queryByText('Second Item')).toBeNull();

    await rerender(
      <AnimatedFlashListItem<TestItem>
        item={item2}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={defaultRenderItem}
      />,
    );

    expect(getByText('Second Item')).toBeTruthy();
  });

  it('updates when index changes', async () => {
    let lastIndex = -1;

    const renderItem = ({ item, index }: AnimatedRenderItemInfo<TestItem>) => {
      lastIndex = index;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    const { rerender } = await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(lastIndex).toBe(0);

    await rerender(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={2}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(lastIndex).toBe(2);
  });

  it('handles isDragEnabled toggle', async () => {
    let dragHandleProps: unknown = null;

    const renderItem = ({ item, dragHandleProps: props }: AnimatedRenderItemInfo<TestItem>) => {
      dragHandleProps = props;
      return (
        <View>
          <Text>{item.title}</Text>
        </View>
      );
    };

    const { rerender } = await render(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={true}
        renderItem={renderItem}
      />,
    );

    expect(dragHandleProps).not.toBeNull();

    await rerender(
      <AnimatedFlashListItem<TestItem>
        item={defaultItem}
        index={0}
        totalItems={5}
        isDragEnabled={false}
        renderItem={renderItem}
      />,
    );

    expect(dragHandleProps).toBeNull();
  });
});
