import React, {
  ForwardedRef,
  ReactNode,
  forwardRef,
  useCallback,
  useImperativeHandle,
  useMemo,
} from 'react';
import {StyleProp, ViewStyle} from 'react-native';
import {TabView as RNTabView} from 'react-native-tab-view';
import {WithCollapsible} from '@components/_core/WithCollapsible';
import {ChipSize} from '@components/_HuGo/Chip/interfaces';
import {useTabTracking} from '@hooks/useTabTracking';
import {CollapsibleInfo} from '@interfaces/generic';

import {
  RNTabBarProps,
  TabBarProps,
  TabBarVariant,
  TabBarVariantType,
  TabViewRef,
  TabViewRenderScene,
  TabViewRoute,
  TVTabBarProps,
} from './interfaces';
import TabBarPillsComponent from './components/TabBarPills';
import TabBarComponent from './components/TabBar';
import {styles} from './styles';
import {TabStoreProvider, useTabStore} from './store';

const TabBarByVariant = {
  [TabBarVariant.pills]: TabBarPillsComponent,
  [TabBarVariant.tabs]: TabBarComponent,
};

interface Props<T extends TabViewRoute> {
  initialIndex?: number;
  routes: T[];
  renderScene: (props: TabViewRenderScene<T>) => ReactNode;
  variant?: TabBarVariantType;
  swipeEnabled?: boolean;
  showTabBar?: boolean;
  style?: StyleProp<ViewStyle>;
  tabBarStyle?: StyleProp<ViewStyle>;
  tabBarProps?: RNTabBarProps;
  lazy?: boolean;
  collapsibleInfo?: CollapsibleInfo;
  onTabPress?: (key: string, selectedTab: string) => void;
  renderLazyPlaceholder?: () => ReactNode;
  onTabChange?: (key: string, index: number) => void;
  renderTabBar?: (props: TVTabBarProps) => ReactNode;
  size?: ChipSize;
}

function InnerTabView<T extends TabViewRoute>(
  {
    routes,
    renderScene,
    tabBarStyle,
    onTabPress,
    style,
    renderLazyPlaceholder,
    renderTabBar: renderTabBarProp,
    onTabChange,
    variant = 'pills',
    size,
    lazy = true,
    showTabBar = true,
    swipeEnabled = true,
    collapsibleInfo,
  }: Props<T>,
  ref: ForwardedRef<TabViewRef>,
) {
  const trackTabUse = useTabTracking();

  const tabIndex = useTabStore(s => s.tabIndex);
  const setTabIndex = useTabStore(s => s.setTabIndex);

  const navigationState = useMemo(
    () => ({index: tabIndex, routes}),
    [tabIndex, routes],
  );

  const goToIndex = useCallback(
    (index: number) => {
      setTabIndex(index);
    },
    [setTabIndex],
  );

  const useImperativeHandleFn = useCallback(
    () => ({
      goToIndex,
      currentIndex: tabIndex,
    }),
    [goToIndex, tabIndex],
  );

  useImperativeHandle(ref, useImperativeHandleFn);

  const handleIndexChange = (indexValue: number): void => {
    if (indexValue !== tabIndex) {
      setTabIndex(indexValue);
      onTabChange?.(routes[indexValue].key, indexValue);
    }
  };

  const handleTabPress = useCallback(
    (key: string, selectedTab: string, module?: string) => {
      module && trackTabUse(module);
      onTabPress?.(key, selectedTab);
    },
    [onTabPress, trackTabUse],
  );

  const renderTabBar = useCallback(
    (props: TVTabBarProps) => {
      const TabBarSelected = TabBarByVariant[variant];
      const TabBar = renderTabBarProp?.(props) || (
        <TabBarSelected
          {...(props as TabBarProps<T>)}
          handleTabPress={handleTabPress}
          size={size}
          style={[styles.tabBar, tabBarStyle]}
        />
      );
      return showTabBar ? (
        collapsibleInfo ? (
          <WithCollapsible collapsibleInfo={collapsibleInfo}>
            {TabBar}
          </WithCollapsible>
        ) : (
          TabBar
        )
      ) : null;
    },
    [
      collapsibleInfo,
      handleTabPress,
      renderTabBarProp,
      showTabBar,
      tabBarStyle,
      variant,
      size,
    ],
  );

  return (
    <RNTabView
      navigationState={navigationState}
      onIndexChange={handleIndexChange}
      renderScene={renderScene}
      renderTabBar={renderTabBar}
      swipeEnabled={swipeEnabled}
      lazy={lazy}
      style={style}
      renderLazyPlaceholder={renderLazyPlaceholder}
    />
  );
}

function _TabView<T extends TabViewRoute>(
  {initialIndex = 0, ...props}: Props<T>,
  ref: ForwardedRef<TabViewRef>,
) {
  return (
    <TabStoreProvider tabIndex={initialIndex}>
      <ForwardedInnerTabView {...props} ref={ref} />
    </TabStoreProvider>
  );
}

const ForwardedInnerTabView = forwardRef(InnerTabView) as <
  T extends TabViewRoute,
>(
  props: Props<T> & {ref?: ForwardedRef<TabViewRef>},
) => React.ReactElement;
/**
 * @deprecated Use `_HuGo/Tabs` instead
 */
const TabViewForwardRef = forwardRef(_TabView);

export {TabViewForwardRef as TabView};

export {SceneMap} from 'react-native-tab-view';

export {TAB_BAR_ITEM_SIZE} from './components/TabBarItem';

export {useCollapsibleHeader} from './hooks/useCollapsibleHeader';

export * from './interfaces';

export * from './store';
