import React from 'react';
import { Text, View } from 'react-native';
import { render } from '@testing-library/react-native';
import { AnimatedFlashList } from '../../AnimatedFlashList';
import type { AnimatedRenderItemInfo } from '../../types';

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

const createTestItems = (count: number): TestItem[] =>
  Array.from({ length: count }, (_, i) => ({
    id: `item-${i + 1}`,
    title: `Test Item ${i + 1}`,
  }));

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

  const defaultKeyExtractor = (item: TestItem) => item.id;

  it('renders without crashing', async () => {
    const data = createTestItems(3);

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('renders items from data array', async () => {
    const data = createTestItems(3);

    const { getByText } = await render(
      <AnimatedFlashList<TestItem>
        data={data}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        estimatedItemSize={80}
      />,
    );

    expect(getByText('Test Item 1')).toBeTruthy();
    expect(getByText('Test Item 2')).toBeTruthy();
    expect(getByText('Test Item 3')).toBeTruthy();
  });

  it('returns null for empty data when no ListFooterComponent', async () => {
    const { toJSON } = await render(
      <AnimatedFlashList<TestItem>
        data={[]}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        estimatedItemSize={80}
      />,
    );

    expect(toJSON()).toBeNull();
  });

  it('renders ListFooterComponent for empty data', async () => {
    const FooterComponent = () => (
      <View testID="footer">
        <Text>No items</Text>
      </View>
    );

    const { getByText } = await render(
      <AnimatedFlashList<TestItem>
        data={[]}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        estimatedItemSize={80}
        ListFooterComponent={FooterComponent}
      />,
    );

    expect(getByText('No items')).toBeTruthy();
  });

  it('supports dragEnabled prop', async () => {
    const data = createTestItems(3);

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          dragEnabled
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('supports dragEnabled=false (disabled)', async () => {
    const data = createTestItems(3);

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          dragEnabled={false}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('calls onReorder callback when provided', async () => {
    const data = createTestItems(3);
    const onReorder = jest.fn();

    await render(
      <AnimatedFlashList<TestItem>
        data={data}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        dragEnabled
        onReorder={onReorder}
        estimatedItemSize={80}
      />,
    );

    // onReorder should be provided but not called until user drags
    expect(onReorder).not.toHaveBeenCalled();
  });

  it('supports canDrag filter function', async () => {
    const data = createTestItems(5);
    const canDrag = (item: TestItem, index: number) => index % 2 === 0;

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          dragEnabled
          canDrag={canDrag}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('supports custom drag config', async () => {
    const data = createTestItems(3);

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          dragEnabled
          config={{
            drag: {
              longPressDuration: 500,
              dragScale: 1.1,
              itemHeight: 100,
            },
          }}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('supports haptic feedback callback', async () => {
    const data = createTestItems(3);
    const onHapticFeedback = jest.fn();

    await render(
      <AnimatedFlashList<TestItem>
        data={data}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        dragEnabled
        onHapticFeedback={onHapticFeedback}
        estimatedItemSize={80}
      />,
    );

    // Haptic feedback should not be called until drag starts
    expect(onHapticFeedback).not.toHaveBeenCalled();
  });

  it('supports onRefresh and refreshing props', async () => {
    const data = createTestItems(3);
    const onRefresh = jest.fn();

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          onRefresh={onRefresh}
          refreshing={false}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('supports onEndReached prop', async () => {
    const data = createTestItems(3);
    const onEndReached = jest.fn();

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          onEndReached={onEndReached}
          onEndReachedThreshold={0.5}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('supports contentContainerStyle prop', async () => {
    const data = createTestItems(3);

    await expect(
      render(
        <AnimatedFlashList<TestItem>
          data={data}
          keyExtractor={defaultKeyExtractor}
          renderItem={defaultRenderItem}
          contentContainerStyle={{ padding: 16 }}
          estimatedItemSize={80}
        />,
      ),
    ).resolves.toBeDefined();
  });

  it('re-renders when data changes', async () => {
    const data1 = createTestItems(2);
    const data2 = createTestItems(4);

    const { rerender, getByText, queryByText } = await render(
      <AnimatedFlashList<TestItem>
        data={data1}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        estimatedItemSize={80}
      />,
    );

    expect(getByText('Test Item 1')).toBeTruthy();
    expect(getByText('Test Item 2')).toBeTruthy();
    expect(queryByText('Test Item 3')).toBeNull();

    await rerender(
      <AnimatedFlashList<TestItem>
        data={data2}
        keyExtractor={defaultKeyExtractor}
        renderItem={defaultRenderItem}
        estimatedItemSize={80}
      />,
    );

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

  it('passes dragHandleProps when drag is enabled', async () => {
    const data = createTestItems(1);
    let receivedDragHandleProps: unknown = null;

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

    await render(
      <AnimatedFlashList<TestItem>
        data={data}
        keyExtractor={defaultKeyExtractor}
        renderItem={renderItem}
        dragEnabled
        estimatedItemSize={80}
      />,
    );

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

  it('passes null dragHandleProps when drag is disabled', async () => {
    const data = createTestItems(1);
    let receivedDragHandleProps: unknown = 'initial';

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

    await render(
      <AnimatedFlashList<TestItem>
        data={data}
        keyExtractor={defaultKeyExtractor}
        renderItem={renderItem}
        dragEnabled={false}
        estimatedItemSize={80}
      />,
    );

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

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

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

    await render(
      <AnimatedFlashList<TestItem>
        data={data}
        keyExtractor={defaultKeyExtractor}
        renderItem={renderItem}
        estimatedItemSize={80}
      />,
    );

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