import React, { ComponentType, useCallback, useMemo } from 'react';
import {
  useLoadTimelineWidgetEffect,
  useStyles,
  useTimelineWidgets,
  useTimelineWidgetActions,
} from '../../hooks';
import {
  StyleSheet,
  ActivityIndicator,
  View,
  ViewStyle,
  TextStyle,
  FlatList,
  ListRenderItem,
} from 'react-native';
import {
  ComponentStyleProp,
  LLComponentStyleFn,
  WidgetMode,
} from '../../types';
import { LLWidget } from './LLWidget';
import { LLWidgetSeparator, LLWidgetSeparatorProps } from './LLWidgetSeparator';
import { WidgetKind } from '@livelike/javascript';
import {
  LLWidgetsLoadMoreButton,
  LLWidgetsLoadMoreButtonProps,
} from './LLWidgetsLoadMoreButton';
import { LLText } from '../LLText';

const UNSUPPORTED_TIMELINE_WIDGET_KIND = [WidgetKind.VIDEO_ALERT];

export type LLWidgetsStyles = {
  container: ViewStyle;
  errorContainer: ViewStyle;
  errorText: TextStyle;
};

export type LLWidgetsProps = ComponentStyleProp<LLWidgetsStyles> & {
  programId: string;
  mode: WidgetMode;
  WidgetComponent?: typeof LLWidget;
  LoadingComponent?: ComponentType<unknown>;
  ErrorComponent?: ComponentType<unknown>;
  LoadMoreComponent?: typeof LLWidgetsLoadMoreButton;
  LoadMoreComponentStyles?: LLWidgetsLoadMoreButtonProps['styles'];
  WidgetSeparatorComponent?: ComponentType<unknown> | typeof LLWidgetSeparator;
  WidgetSeparatorComponentStyles?: LLWidgetSeparatorProps['styles'];
  onLoadMore?: () => void;
};

type WidgetItem = {
  widgetId: string;
  widgetKind: WidgetKind | undefined;
  isSeparator?: boolean;
};

export function LLWidgets({
  programId,
  mode,
  onLoadMore: onLoadMoreProp,
  WidgetComponent = LLWidget,
  LoadingComponent,
  ErrorComponent,
  LoadMoreComponent = LLWidgetsLoadMoreButton,
  WidgetSeparatorComponent = LLWidgetSeparator,
  WidgetSeparatorComponentStyles,
  LoadMoreComponentStyles,
  styles: stylesProp,
}: LLWidgetsProps) {
  const styles = useStyles({
    componentStylesFn: getWidgetsStyles,
    stylesProp,
  });
  const { isLoading, error, onLoadMore } = useLoadTimelineWidgetEffect({
    programId,
    mode,
  });
  const widgets = useTimelineWidgets({ programId });
  const { onWidgetInteractiveTimeout } = useTimelineWidgetActions({
    programId,
  });
  const widgetsWithSeparator = useMemo(() => {
    if (!widgets?.length) return [];
    return widgets.reduce((_widgets: WidgetItem[], widget, index) => {
      if (index !== 0) {
        _widgets.push({
          widgetId: `separator-${index}`,
          widgetKind: undefined,
          isSeparator: true,
        });
      }
      _widgets.push(widget);
      return _widgets;
    }, []);
  }, [widgets]);

  const onLoadMoreHandler = useCallback(() => {
    return onLoadMore?.().then(() => {
      onLoadMoreProp?.();
    });
  }, [onLoadMore, onLoadMoreProp]);

  const renderItem: ListRenderItem<WidgetItem> = useCallback(
    ({ item }) => {
      if (item.isSeparator) {
        return !UNSUPPORTED_TIMELINE_WIDGET_KIND.includes(item.widgetKind) ? (
          <WidgetSeparatorComponent styles={WidgetSeparatorComponentStyles} />
        ) : null;
      }
      return (
        <WidgetComponent
          programId={programId}
          widgetId={item.widgetId}
          widgetKind={item.widgetKind}
          interactiveTimeout={null}
        />
      );
    },
    [
      programId,
      WidgetComponent,
      WidgetSeparatorComponent,
      WidgetSeparatorComponentStyles,
    ]
  );

  if (isLoading) {
    return LoadingComponent ? (
      <LoadingComponent />
    ) : (
      <ActivityIndicator size={'small'} color="blue" />
    );
  }

  if (error) {
    return ErrorComponent ? (
      <ErrorComponent />
    ) : (
      <View style={styles.errorContainer}>
        <LLText style={styles.errorText}>{'Unable to load widgets'}</LLText>
      </View>
    );
  }

  const isPopupMode = mode === WidgetMode.POPUP;

  if (isPopupMode) {
    if (!widgets?.length) {
      return undefined;
    }
    // when in popup mode only show last older widget from widgets array,
    // when widget interactive timeouts or widget is dismissed, last older widget
    // is removed from list and then the last array item would point to second last older widget
    // This way we show one widget at a time in pop up mode.
    const { widgetId, widgetKind } = widgets[widgets.length - 1];
    return (
      <WidgetComponent
        key={widgetId}
        programId={programId}
        widgetId={widgetId}
        widgetKind={widgetKind}
        onDismiss={() => onWidgetInteractiveTimeout(widgetId)}
        onInteractiveTimeout={() => onWidgetInteractiveTimeout(widgetId)}
      />
    );
  }

  return (
    <FlatList
      style={styles.container}
      data={widgetsWithSeparator}
      renderItem={renderItem}
      keyExtractor={(item) => item.widgetId}
      ListFooterComponent={
        onLoadMore ? (
          <LoadMoreComponent
            onPress={onLoadMoreHandler}
            styles={LoadMoreComponentStyles}
          />
        ) : null
      }
    />
  );
}

const getWidgetsStyles: LLComponentStyleFn<LLWidgetsStyles> = ({ theme }) =>
  StyleSheet.create({
    container: {
      flex: 1,
    },
    errorContainer: {
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
    },
    errorText: {
      color: theme.error,
    },
  });
