import * as React from 'react';
import { RefreshControl, ScrollView, View, ViewStyle } from 'react-native';

import { useEnabler } from '~/hooks';
import { setImmersiveColor } from '~/navigation';
import { withSafeArea } from '~/providers/with-safe-area';
import { testIdProps } from '~/test/utils';
import { enhanceViewStyle } from '~/utils/styles';

import { isNonScrolling, presets } from './screen.presets';
import { ScreenProps as Props } from './screen.props';

/**
 * Screen without a scroll view.
 *
 * @param {boolean} safe True to wrap screen in SafeAreaView. Defaults to false.
 * @param {string} componentId Screen component id.
 * @param {Preset} preset One of the different types of presets.
 * @param {ColorValue} backgroundColor An optional background color.
 * @param {Preset} statusBarStyle An optional status bar setting. Defaults to light.
 * @param {ReactNode} children Children component.
 * @param {ViewStyle} style An optional style override useful for content padding & margin.
 * @param {ViewStyle} outerStyle An optional style override useful for padding & margin.
 */
export const ScreenWithoutScrolling: React.FC<Props> = (props) => {
  const { backgroundColor, children, safe, style: styleOverride, ...rest } = props;
  const preset = presets.fixed;
  const style = styleOverride || {};
  const backgroundStyle: ViewStyle = backgroundColor ? { backgroundColor } : {};
  const containerStyle = enhanceViewStyle([preset.outer, style, backgroundStyle]);
  const screenContent = (
    <View style={containerStyle} {...rest}>
      {children}
    </View>
  );

  return safe
    ? withSafeArea(screenContent, backgroundColor || style.backgroundColor)
    : screenContent;
};

/**
 * Scroll with a scroll view.
 *
 * @param {boolean} safe True to wrap screen in SafeAreaView. Defaults to false.
 * @param {string} componentId Screen component id.
 * @param {Preset} preset One of the different types of presets.
 * @param {ColorValue} backgroundColor An optional background color.
 * @param {Preset} statusBarStyle An optional status bar setting. Defaults to light.
 * @param {ReactNode} children Children component.
 * @param {ViewStyle} style An optional style override useful for content padding & margin.
 * @param {ViewStyle} outerStyle An optional style override useful for padding & margin.
 */
export const ScreenWithScrolling: React.FC<Props> = (props) => {
  const {
    backgroundColor,
    children,
    onRefresh,
    outerStyle,
    safe,
    style: styleOverride,
    ...rest
  } = props;
  const { enabled: refreshing, enable: showRefresh, disable: hideRefresh } = useEnabler(false);
  const preset = presets.scroll;
  const style = styleOverride || {};
  const backgroundStyle: ViewStyle = backgroundColor ? { backgroundColor } : null;
  const containerStyle = enhanceViewStyle([preset.outer, outerStyle, backgroundStyle]);

  async function handleRefresh() {
    if (typeof onRefresh === 'function') {
      showRefresh();
      await onRefresh();
      hideRefresh();
    }
  }

  const refreshControl = React.useMemo(
    () =>
      onRefresh ? (
        <RefreshControl
          refreshing={refreshing}
          onRefresh={handleRefresh}
          {...testIdProps('screen-refresh-control')}
        />
      ) : null,
    [onRefresh, refreshing],
  );

  const screenContent = (
    <ScrollView
      contentContainerStyle={[preset.inner, style]}
      keyboardShouldPersistTaps="handled"
      refreshControl={refreshControl}
      style={containerStyle}
      {...rest}
    >
      {children}
    </ScrollView>
  );

  return safe
    ? withSafeArea(screenContent, backgroundColor || style.backgroundColor)
    : screenContent;
};

/**
 * The starting component on every screen in the app.
 *
 * @param {boolean} safe True to wrap screen in SafeAreaView. Defaults to false.
 * @param {string} componentId Screen component id.
 * @param {Preset} preset One of the different types of presets.
 * @param {ColorValue} backgroundColor An optional background color.
 * @param {Preset} statusBarStyle An optional status bar setting. Defaults to light.
 * @param {ReactNode} children Children component.
 * @param {ViewStyle} style An optional style override useful for content padding & margin.
 * @param {ViewStyle} outerStyle An optional style override useful for padding & margin.
 */
export const Screen: React.FC<Props> = (props) => {
  const { backgroundColor, componentId, statusBarStyle } = props;

  React.useEffect(
    function () {
      setImmersiveColor(componentId, statusBarStyle, backgroundColor);
    },
    [statusBarStyle, backgroundColor],
  );

  if (isNonScrolling(props.preset)) {
    return <ScreenWithoutScrolling {...props} />;
  } else {
    return <ScreenWithScrolling {...props} />;
  }
};

Screen.defaultProps = {
  safe: false,
  statusBarStyle: 'light',
};
