import { useTheme } from '@emotion/react';
import type { ReactElement, ReactNode } from 'react';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import type { StyleProp, ViewProps, ViewStyle } from 'react-native';
import { Animated, TouchableWithoutFeedback, View } from 'react-native';
import type { PagerViewOnPageScrollEventData } from 'react-native-pager-view';
import PagerView from 'react-native-pager-view';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import type { IconName } from '../Icon';
import Typography from '../Typography';
import ActiveTabIndicator from './ActiveTabIndicator';
import SceneView from './SceneView';
import ScrollableTabs from './ScrollableTabs';
import ScrollableTabHeader from './ScrollableTabsHeader/ScrollableTabsHeader';
import {
  HeaderTab,
  HeaderTabItem,
  HeaderTabWrapper,
  TabContainer,
} from './StyledTabs';
import type { BadgeConfigType } from './TabWithBadge';
import TabWithBadge from './TabWithBadge';
import useHandlePageScroll from './useHandlePageScroll';
import { ScreenContext, TabContext, useIsFocused } from './useIsFocused';

export type ItemType =
  | string
  | IconName
  | ((props: { color: string }) => ReactNode);

export type TabType = {
  key: string;
  activeItem: ItemType;
  inactiveItem?: ItemType;
  component: ReactNode;
  testID?: string;
  badge?: BadgeConfigType;
};

export interface TabsHeaderProps {
  tabs: TabType[];
  selectedTabKey: string;
  onTabPress: (key: string) => void;
  barStyle?: StyleProp<ViewStyle>;
  insets: { top: number; right: number; bottom: number; left: number };
  componentTestID?: string;
  tabsWidth: number;
  setTabsWidth: (width: number) => void;
  positionAnimatedValue: Animated.Value;
  scrollOffsetAnimatedValue: Animated.Value;
}
export interface TabsProps extends ViewProps {
  /**
   * Callback which is called on tab press, receiving key of upcoming active Tab.
   */
  onTabPress: (key: string) => void;
  /**
   * Current selected tab key.
   */
  selectedTabKey: string;
  /**
   * List of Tabs to be rendered. Each Tab must have an unique key.
   */
  tabs: TabType[];
  /**
   * Style for the container of Tab.
   */
  containerStyle?: StyleProp<ViewStyle>;
  /**
   * Style for the tab navigation bar.
   */
  barStyle?: StyleProp<ViewStyle>;
  /**
   * Whether inactive screen should be removed and unmounted in
   * Defaults value is `false`.
   */
  lazy?: boolean;
  /**
   * Only work when lazy is `true`. You can specify how many adjacent screens should be preloaded.
   * Defaults value is `1`.
   */
  lazyPreloadDistance?: number;
  /**
   * Boolean indicating whether to enable swipe gestures. Passing `false` will disable swipe gestures, but the user can still switch tabs by pressing the tab bar.
   * Defaults value is `true`.
   */
  swipeEnabled?: boolean;
  /**
   * Testing id of the component.
   */
  testID?: string;
  /**
   * Custom header component.
   */
  header?: (props: TabsHeaderProps) => ReactElement;
}

const AnimatedPagerView = Animated.createAnimatedComponent(PagerView);

const getTabItem = ({
  item,
  color,
  active,
}: {
  item: ItemType;
  color: string;
  active: boolean;
}) => {
  if (typeof item === 'string') {
    return (
      <Typography.Body
        variant={active ? 'small-bold' : 'small'}
        numberOfLines={1}
        style={{ color }}
      >
        {item}
      </Typography.Body>
    );
  }

  return item({ color });
};

const TabsHeader = ({
  tabs,
  selectedTabKey,
  onTabPress,
  barStyle,
  insets,
  componentTestID,
  tabsWidth,
  setTabsWidth,
  positionAnimatedValue,
  scrollOffsetAnimatedValue,
}: TabsHeaderProps) => {
  const theme = useTheme();
  return (
    <HeaderTabWrapper
      themeInsets={insets}
      style={barStyle}
      testID={componentTestID ? `${componentTestID}-tab-bar` : undefined}
    >
      <View>
        <HeaderTab
          onLayout={(e) => {
            const { width } = e.nativeEvent.layout;
            if (tabsWidth !== width) {
              setTabsWidth(width);
            }
          }}
        >
          {tabs.map((tab) => {
            const {
              key,
              testID,
              activeItem,
              inactiveItem: originalInactiveItem,
              badge,
            } = tab;

            const active = selectedTabKey === key;
            const inactiveItem = originalInactiveItem ?? activeItem;
            const tabItem = getTabItem({
              item: active ? activeItem : inactiveItem,
              color: theme.__hd__.tabs.colors.text,
              active,
            });

            return (
              <TouchableWithoutFeedback
                key={key}
                onPress={() => {
                  onTabPress(key);
                }}
                testID={testID}
              >
                <HeaderTabItem>
                  <TabWithBadge config={badge} tabItem={tabItem} />
                </HeaderTabItem>
              </TouchableWithoutFeedback>
            );
          })}
        </HeaderTab>
        <ActiveTabIndicator
          positionAnimatedValue={positionAnimatedValue}
          scrollOffsetAnimatedValue={scrollOffsetAnimatedValue}
          tabsLength={tabs.length}
          tabsWidth={tabsWidth}
        />
      </View>
    </HeaderTabWrapper>
  );
};

const Tabs = ({
  onTabPress,
  selectedTabKey,
  tabs,
  containerStyle,
  barStyle,
  lazy = false,
  lazyPreloadDistance = 1,
  swipeEnabled = true,
  testID: componentTestID,
  header,
}: TabsProps): ReactElement => {
  const insets = useSafeAreaInsets();
  const pagerViewRef = useRef<PagerView>(null);
  const selectedTabIndex = tabs.findIndex(
    (item) => item.key === selectedTabKey
  );
  const scrollOffsetAnimatedValue = useRef(new Animated.Value(0)).current;
  const positionAnimatedValue = useRef(new Animated.Value(0)).current;
  const [tabsWidth, setTabsWidth] = useState<number>(0);

  const { onPageScrollStateChanged, hasScrolled } = useHandlePageScroll();

  useEffect(() => {
    if (selectedTabIndex !== -1) {
      pagerViewRef.current?.setPage(selectedTabIndex);
    }
  }, [selectedTabIndex]);

  const tabContextProviderValue = useMemo(
    () => ({
      selectedTabKey,
    }),
    [selectedTabKey]
  );

  const headerProps = useMemo(
    () => ({
      tabs,
      selectedTabKey,
      onTabPress,
      barStyle,
      insets,
      componentTestID,
      tabsWidth,
      setTabsWidth,
      positionAnimatedValue,
      scrollOffsetAnimatedValue,
    }),
    [
      tabs,
      selectedTabKey,
      onTabPress,
      barStyle,
      insets,
      componentTestID,
      tabsWidth,
      setTabsWidth,
      positionAnimatedValue,
      scrollOffsetAnimatedValue,
    ]
  );

  return (
    <TabContext.Provider value={tabContextProviderValue}>
      <TabContainer style={containerStyle} testID={componentTestID}>
        {header ? header(headerProps) : <TabsHeader {...headerProps} />}
        <AnimatedPagerView
          initialPage={selectedTabIndex}
          ref={pagerViewRef}
          onPageSelected={(e) => {
            const index = e.nativeEvent.position;
            const selectedItem = tabs[index];
            if (hasScrolled.current && selectedItem) {
              onTabPress(selectedItem.key);
            }
          }}
          onPageScrollStateChanged={onPageScrollStateChanged}
          onPageScroll={Animated.event<PagerViewOnPageScrollEventData>(
            [
              {
                nativeEvent: {
                  offset: scrollOffsetAnimatedValue,
                  position: positionAnimatedValue,
                },
              },
            ],
            {
              useNativeDriver: true,
            }
          )}
          scrollEnabled={swipeEnabled}
          style={{ flex: 1 }}
        >
          {tabs.map((tab, index) => {
            const { key, component, testID } = tab;

            return (
              <ScreenContext.Provider value={key} key={key}>
                <SceneView
                  testID={testID}
                  key={key}
                  index={index}
                  selectedIndex={selectedTabIndex}
                  lazy={lazy}
                  lazyPreloadDistance={lazyPreloadDistance}
                >
                  {component}
                </SceneView>
              </ScreenContext.Provider>
            );
          })}
        </AnimatedPagerView>
      </TabContainer>
    </TabContext.Provider>
  );
};

export default Object.assign(Tabs, {
  Header: TabsHeader,
  Scroll: ScrollableTabs,
  ScrollHeader: ScrollableTabHeader,
  useIsFocused,
});
