/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React, {useEffect, useMemo, useRef, useState, useCallback} from 'react';
import {
  View,
  SectionList,
  StyleSheet,
  Text,
  SectionListProps,
  RefreshControl,
  Platform,
  ViewabilityConfig,
} from 'react-native';
import {TestSuite} from '@rnoh/testerino';
import {Button, ObjectDisplayer, TestCase} from '../components';

interface SectionData {
  id: string;
  title: string;
  data: string[];
}

const DATA: SectionData[] = [
  {
    id: '0',
    title: 'Main dishes',
    data: ['Pizza', 'Burger', 'Risotto'],
  },
  {
    id: '1',
    title: 'Sides',
    data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
  },
  {
    id: '2',
    title: 'Drinks',
    data: ['Water', 'Coke', 'Beer'],
  },
  {
    id: '3',
    title: 'Desserts',
    data: ['Cheese Cake', 'Ice Cream'],
  },
];

const commonProps = {
  style: {width: 256},
  sections: DATA,
  keyExtractor: (item, index) => `${item.id}-${index}`,
  renderSectionHeader: ({section}) => (
    <Text style={styles.title}>{section.title}</Text>
  ),
  renderItem: ({item}) => (
    <View style={styles.item}>
      <Text style={styles.title}>{item}</Text>
    </View>
  ),
} satisfies SectionListProps<any>;

export const SectionListTest = () => {
  return (
    <TestSuite name="SectionList">
      <TestCase.Example modal itShould="display items in the SectionList">
        <View style={styles.container}>
          <SectionList
            sections={DATA}
            keyExtractor={(item, index) => `${item}-${index}`}
            renderItem={({item}) => (
              <View style={styles.item}>
                <Text style={styles.title}>{item}</Text>
              </View>
            )}
            renderSectionHeader={({section: {title}}) => (
              <Text style={styles.title}>{title}</Text>
            )}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example modal itShould="display an array of visible items">
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  viewabilityConfig={{viewAreaCoveragePercentThreshold: 100}}
                  onViewableItemsChanged={item => {
                    setObject(item.viewableItems.map(i => i.item));
                  }}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="render no more than 2 new items per batch when scrolling down">
        <View style={{height: 200}}>
          <SectionList
            {...commonProps}
            windowSize={2}
            renderItem={({item}) => {
              return (
                <DelayedDisplayer
                  delayInMs={1000}
                  renderContent={() => {
                    return (
                      <View style={styles.item}>
                        <Text style={styles.title}>{item}</Text>
                      </View>
                    );
                  }}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="display nativeEvent when onMomentumScrollBegin">
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  onMomentumScrollBegin={e => {
                    setObject({nativeEvent: e.nativeEvent});
                  }}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="display nativeEvent when onMomentumScrollEnd">
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  onMomentumScrollEnd={e => {
                    setObject({nativeEvent: e.nativeEvent});
                  }}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="display event sent to by onScrollToIndexFailed when pressing the button before scrolling">
        <ScrollToIndexFailureTestCase />
      </TestCase.Example>
      {/* sticky headers seems to work on Android when App.tsx was replaced with content of this test */}
      <TestCase.Example
        modal
        itShould="stick section headers (fails on Android when fabric is enabled)"
        skip={{android: true, harmony: false}}>
        <View style={styles.container}>
          <SectionList {...commonProps} stickySectionHeadersEnabled />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="support viewOffset when scrolling to location">
        <View style={styles.container}>
          <ScrollToLocationOffset />
        </View>
      </TestCase.Example>
      <TestCase.Example modal itShould="show vertical scroll indicator">
        <View style={styles.container}>
          <SectionList {...commonProps} showsVerticalScrollIndicator={true} />
        </View>
      </TestCase.Example>
      <TestCase.Example modal itShould="hide vertical scroll indicator">
        <View style={styles.container}>
          <SectionList {...commonProps} showsVerticalScrollIndicator={false} />
        </View>
      </TestCase.Example>
      <TestCase.Example modal itShould="show horizontal scroll indicator">
        <View style={styles.container}>
          <View style={{width: 200, height: '100%'}}>
            <SectionList
              {...commonProps}
              showsHorizontalScrollIndicator={true}
              horizontal
            />
          </View>
        </View>
      </TestCase.Example>
      <TestCase.Example modal itShould="hide horizontal scroll indicator">
        <View style={styles.container}>
          <View style={{width: 200, height: '100%'}}>
            <SectionList
              {...commonProps}
              showsHorizontalScrollIndicator={false}
              horizontal
            />
          </View>
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="display overscroll effect"
        skip={{android: false, harmony: true}}>
        <View style={styles.container}>
          {/* On Android this settings enables stretching the ScrollView content. On Harmony `bounces` prop can be used instead. */}
          <SectionList {...commonProps} overScrollMode="always" />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="render custom RefreshControl on pull to refresh">
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  refreshControl={
                    // only RefreshControl can be provided
                    <RefreshControl
                      onRefresh={() => {
                        setObject({onRefreshCalled: true});
                      }}
                      refreshing={false}
                      tintColor={'red'} // iOS.
                      colors={['red', 'green']} // Android
                    />
                  }
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="render standard RefreshControl on pull to refresh">
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  onRefresh={() => {
                    setObject({onRefreshCalled: true});
                  }}
                  refreshing={false}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="display onScroll native event throttled every second"
        skip={Platform.select({
          android: 'RN bug', // https://github.com/facebook/react-native/issues/18441
        })}>
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  scrollEventThrottle={1000}
                  onScroll={e => {
                    setObject(e.nativeEvent);
                  }}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="allow scrolling beneath the content due to large lengths returned in getItemLayout">
        <View style={styles.container}>
          <SectionList
            {...commonProps}
            getItemLayout={(data, index) => {
              const ITEM_HEIGHT = 1000;
              return {
                length: ITEM_HEIGHT,
                offset: ITEM_HEIGHT * index,
                index,
              };
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="display onEndReached event when scroll reached bottom and onStartReached event when scroll reached top">
        <View style={styles.container}>
          <ObjectDisplayer
            renderContent={setObject => {
              return (
                <SectionList
                  {...commonProps}
                  onEndReached={e => {
                    setObject(e);
                  }}
                  onStartReached={e => {
                    setObject(e);
                  }}
                />
              );
            }}
          />
        </View>
      </TestCase.Example>
      <TestCase.Example
        modal
        itShould="click on 'Record interaction' button changes the first three items background color to blue">
        <View style={{height: 500}}>
          <SectionListRecordInteractionTest />
        </View>
      </TestCase.Example>
      <TestCase.Example modal itShould="display smooth scroll offset updates">
        <SectionListSmoothScrollOffsetExample />
      </TestCase.Example>
    </TestSuite>
  );
};

function ScrollToIndexFailureTestCase() {
  const ref = useRef<SectionList>(null);

  return (
    <View style={{height: 500}}>
      <Button
        label="Scroll to index"
        onPress={() => {
          if (ref.current) {
            ref.current.scrollToLocation({
              sectionIndex: 1,
              itemIndex: 10,
              animated: true,
            });
          }
        }}
      />
      <View style={{flex: 1}}>
        <ObjectDisplayer
          renderContent={setObject => {
            return (
              <SectionList
                ref={ref}
                {...commonProps}
                windowSize={1}
                onScrollToIndexFailed={info => {
                  setObject(info);
                }}
              />
            );
          }}
        />
      </View>
    </View>
  );
}

function ScrollToLocationOffset() {
  const ref = useRef<SectionList>(null);

  return (
    <>
      <Button
        label="Scroll to onion rings"
        onPress={() => {
          ref.current?.scrollToLocation({
            itemIndex: 1,
            sectionIndex: 1,
            viewOffset: -100,
          });
        }}
      />
      <SectionList ref={ref} {...commonProps} />
    </>
  );
}

function DelayedDisplayer(props: {
  renderContent: () => any;
  delayInMs: number;
}) {
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const timeout = setTimeout(() => {
      setIsVisible(true);
    }, props.delayInMs);
    return () => {
      clearTimeout(timeout);
    };
  }, []);

  return <>{isVisible ? props.renderContent() : null}</>;
}

export interface ViewTokenItem<TItem> {
  item: TItem;
  key: string;
  index: number | null;
  isViewable: boolean;
  section?: any | undefined;
}

type OnViewableItemsChangedType<TItem> = {
  viewableItems: Array<ViewTokenItem<TItem>>;
  changed: Array<ViewTokenItem<TItem>>;
};

function SectionListRecordInteractionTest() {
  const [visibleItems, setVisibleItems] = useState<string[]>([]);

  const ref = useRef<SectionList>(null);

  const defaultViewabilityConfig: ViewabilityConfig = useMemo(() => {
    return {
      waitForInteraction: true,
      minimumViewTime: 100,
      itemVisiblePercentThreshold: 70,
    };
  }, []);

  const handleOnPress = () => {
    if (ref.current) {
      ref.current.recordInteraction();
    }
  };

  const onViewableItemsChanged = ({
    viewableItems,
  }: OnViewableItemsChangedType<string>) => {
    const newVisibleItems = viewableItems
      .filter(viewableItem => viewableItem.index != null)
      .map(viewableItem => viewableItem.item);

    setVisibleItems(newVisibleItems);
  };

  return (
    <>
      <View style={{marginBottom: 10}}>
        <Button label="Record interaction" onPress={handleOnPress} />
        <Text style={{padding: 10}}>
          Visible Items are: {JSON.stringify(visibleItems)}
        </Text>
      </View>
      <SectionList
        ref={ref}
        sections={DATA}
        scrollEnabled={false}
        keyExtractor={(_, index) => String(index)}
        renderSectionHeader={({section}) => (
          <Text style={styles.title}>{section.title}</Text>
        )}
        renderItem={({item}) => (
          <View
            style={[
              {
                height: 200,
                backgroundColor: 'lightblue',
                marginBottom: 10,
              },
              visibleItems.includes(item) && {backgroundColor: 'blue'},
            ]}>
            <Text
              style={{
                fontSize: 32,
                height: 200,
                textAlign: 'center',
                textAlignVertical: 'center',
              }}>
              {item}
            </Text>
          </View>
        )}
        viewabilityConfig={defaultViewabilityConfig}
        onViewableItemsChanged={onViewableItemsChanged}
      />
    </>
  );
}

function SectionListSmoothScrollOffsetExample() {
  const [headerPageY, setHeaderPageY] = React.useState(0);
  const headerRef = React.useRef<View>(null);

  const measureHeader = useCallback(() => {
    headerRef.current?.measure?.((_x, _y, _width, _height, _pageX, pageY) => {
      setHeaderPageY(pageY);
    });
  }, []);

  const customRenderSectionHeader: SectionListProps<string>['renderSectionHeader'] =
    ({section}) =>
      section.title === DATA[0].title ? (
        <View ref={headerRef} style={styles.title}>
          <Text style={styles.title}>{section.title}</Text>
        </View>
      ) : (
        <Text style={styles.title}>{section.title}</Text>
      );

  return (
    <View style={styles.container}>
      <Text style={{marginBottom: 8, fontSize: 16}}>
        Header PageY: {headerPageY.toFixed(2)}
      </Text>
      <SectionList
        {...commonProps}
        renderSectionHeader={customRenderSectionHeader}
        onScroll={measureHeader}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    height: 300,
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 32,
    height: 40,
  },
});

export default SectionListTest;