import React, { forwardRef, useMemo, useState } from 'react';
import {
  Animated,
  Modal as RNModal,
  RefreshControl,
  StatusBar,
  StyleSheet,
  TouchableOpacity,
} from 'react-native';

import useTheme from '../../context/theme/useTheme';
import { useStatusBarHeight } from '../../hooks/useHeaderHeight';
import useModal, { UseModalType } from '../../hooks/useModal';

import type { ModalProps, PositionType } from './types';
import { Box } from '../Box';
import createSxStyle from '../../lib/sx';
import { SxProps } from '../../lib/styleDictionary';
import { Header } from './Header';
import { Content } from './Content';
import { Footer } from './Footer';
import { Button } from '../Button';
import { CloseIcon } from '../../icons';
import { ScrollView } from '../ScrollView';

type ExportComponent = {
  useModal: (initial?: boolean) => UseModalType;
  Header: typeof Header;
  Content: typeof Content;
  Footer: typeof Footer;
};

const DEFAULT_ANIMATIONS = {
  fade: 0,
  fadeMask: 0,
  translate: 250,
};

const RenderModal = forwardRef<RNModal, ModalProps>(
  (
    {
      children,
      hiddenBar,
      visible: isOpen,
      onClose,
      fullScreen,
      styles,
      sx,
      extra,
      closeIcon,
      header,
      scrollProps,
      width,
      removePaddingRoot,
      closable = true,
      scrollable = false,
      maskClosable = true,
      statusHeight: _statusHeight,
      maskComponent: MaskComponent = TouchableOpacity,
      position = 'center',
      animationType = position !== 'bottom' ? 'fade' : 'slide',
      ...rest
    },
    ref
  ) => {
    const [visible, setVisible] = useState(false);

    const translate = React.useRef(
      new Animated.Value(DEFAULT_ANIMATIONS.translate)
    ).current;
    const fadeMask = React.useRef(
      new Animated.Value(DEFAULT_ANIMATIONS.fadeMask)
    ).current;
    const fade = React.useRef(
      new Animated.Value(DEFAULT_ANIMATIONS.fade)
    ).current;

    const theme = useTheme();

    const statusBarHeightCalc = useStatusBarHeight();
    const statusBarHeight = _statusHeight ?? statusBarHeightCalc;

    const isTop = useMemo(() => position === 'top', [position]);
    const isBottom = useMemo(() => position === 'bottom', [position]);
    const isEnabledAnimation = useMemo(
      () => animationType !== 'none',
      [animationType]
    );

    React.useEffect(() => {
      if (isOpen) {
        setVisible(true);
        if (isEnabledAnimation) {
          if (isBottom) {
            Animated.parallel([
              Animated.timing(fadeMask, {
                toValue: 1,
                duration: 200,
                useNativeDriver: true,
              }),
              Animated.timing(translate, {
                toValue: 0,
                duration: 200,
                useNativeDriver: false,
              }),
              Animated.timing(fade, {
                toValue: 1,
                duration: 100,
                delay: 150,
                useNativeDriver: true,
              }),
            ]).start();
          } else {
            Animated.parallel([
              Animated.timing(fadeMask, {
                toValue: 1,
                duration: 250,
                useNativeDriver: true,
              }),
              Animated.timing(fade, {
                toValue: 1,
                duration: 100,
                // delay: 200,
                useNativeDriver: true,
              }),
            ]).start();
          }
        }
      } else if (!isOpen) {
        if (isEnabledAnimation) {
          if (isBottom) {
            Animated.parallel([
              Animated.timing(fadeMask, {
                toValue: DEFAULT_ANIMATIONS.fadeMask,
                duration: 150,
                useNativeDriver: true,
              }),
              Animated.timing(translate, {
                toValue: DEFAULT_ANIMATIONS.translate,
                duration: 200,
                useNativeDriver: false,
              }),
              Animated.timing(fade, {
                toValue: DEFAULT_ANIMATIONS.fade,
                duration: 100,
                // delay: 200,
                useNativeDriver: true,
              }),
            ]).start(({ finished }) => {
              if (finished) {
                setVisible(false);
              }
            });
          } else {
            Animated.parallel([
              Animated.timing(fadeMask, {
                toValue: DEFAULT_ANIMATIONS.fadeMask,
                isInteraction: false,
                duration: 250,
                useNativeDriver: true,
              }),
              Animated.timing(fade, {
                toValue: DEFAULT_ANIMATIONS.fade,
                isInteraction: false,
                duration: 100,
                // delay:  200,
                useNativeDriver: true,
              }),
            ]).start(({ finished }) => {
              if (finished) {
                setVisible(false);
              }
            });
          }
        } else {
          setVisible(false);
        }
      }
    }, [fade, fadeMask, isBottom, isEnabledAnimation, isOpen, translate]);

    const icon = closeIcon || (
      <CloseIcon
        size={18}
        fill="text"
        sx={sx?.closeIcon}
        style={styles?.closeIcon}
      />
    );

    const rounded = fullScreen ? 0 : ('modal' as const);

    const paddingTopWithStatusBar = useMemo(() => {
      return Number(
        ((statusBarHeight || theme.spacing) / theme.spacing).toFixed(2)
      );
    }, [statusBarHeight, theme.spacing]);

    return (
      <RNModal ref={ref} transparent visible={visible} hardwareAccelerated>
        <Box
          width="100%"
          height="100%"
          sx={positionSx[fullScreen ? 'top' : position]}
        >
          {hiddenBar && <StatusBar hidden />}

          <MaskComponent
            id="modal-mask"
            onPress={maskClosable ? onClose : undefined}
            activeOpacity={1}
            style={createSxStyle(
              {
                position: 'absolute',
                zIndex: 1,
                flex: 1,
                height: theme.height,
                width: theme.width,
                bg: fullScreen ? 'modal' : 'modalMask',
                ...positionSx[position],
                top: 0,
                style: styles?.mask,
                sx: sx?.mask,
              },
              theme
            )}
          />

          {/* Modal root container */}
          <Animated.View
            id="modal-root"
            style={createSxStyle(
              {
                style: styles?.root,
                sx: {
                  zIndex: 1,
                  position: 'relative',
                  // height: modalMaskHeight,
                  height: fullScreen ? '100%' : undefined,
                  flexShrink: 1,
                  width: width ?? theme?.maxWidth,
                  p: removePaddingRoot || fullScreen ? 0 : 1,
                  pt:
                    fullScreen || !removePaddingRoot || scrollable
                      ? paddingTopWithStatusBar
                      : 0,
                  pb: fullScreen || removePaddingRoot ? 0 : 1,
                  mx: 'auto',
                  alignItems: 'center',
                  overflow: 'hidden',
                  ...positionSx[fullScreen ? 'top' : position],
                  ...sx?.root,
                },
              },
              theme
            )}
          >
            {/* content apply animation */}
            <Animated.View
              id="modal-container"
              style={createSxStyle(
                {
                  bg: 'modal',
                  width: '100%',
                  zIndex: 2,
                  overflow: 'hidden',
                  flexShrink: 1,
                  flex: fullScreen ? 1 : undefined,
                  roundedTop: rounded,
                  roundedBottom: rounded,
                  sx: sx?.container,
                  style: [
                    isBottom &&
                      isEnabledAnimation && {
                        transform: [{ translateY: translate }],
                      },
                    !isBottom &&
                      isEnabledAnimation && {
                        opacity: fadeMask,
                      },
                    styles?.container,
                  ],
                },
                theme
              )}
            >
              {!!header && (
                <Animated.View
                  style={createSxStyle(
                    {
                      opacity: fade,
                      width: '100%',
                    },
                    theme
                  )}
                >
                  {header}
                </Animated.View>
              )}

              {closable && (
                <Button
                  onlyIcon
                  type="link"
                  appearance="text"
                  onPress={onClose}
                  position="absolute"
                  size="small"
                  top={15}
                  right={10}
                  zIndex="max"
                  Component={TouchableOpacity}
                >
                  {icon}
                </Button>
              )}

              <ScrollView
                id="modal-body"
                style={createSxStyle(
                  {
                    // flex: fullScreen ? 1 : undefined,
                    // flexGrow: fullScreen ? 1 : 0,
                    sx: sx?.content,
                    ...sx,
                    ...rest,
                    style: [styles?.content],
                  },
                  theme
                )}
                contentContainerStyle={createSxStyle(
                  {
                    styles: styles?.contentScroll,
                    sx: sx?.contentScroll,
                    bg: 'modal',
                  },
                  theme
                )}
                refreshControl={
                  <RefreshControl
                    style={{ opacity: 0 }}
                    refreshing={false}
                    children={<Box />}
                    onRefresh={onClose}
                  />
                }
                {...scrollProps}
              >
                {children}
              </ScrollView>
            </Animated.View>

            {extra && (
              <Animated.View
                style={StyleSheet.flatten([
                  {
                    width: '100%',
                    zIndex: 2,
                    opacity: fade,
                  },
                ])}
              >
                {extra}
              </Animated.View>
            )}
          </Animated.View>
        </Box>
      </RNModal>
    );
  }
);

const positionSx: Record<PositionType, SxProps> = {
  top: {
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  center: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  bottom: {
    justifyContent: 'flex-end',
    alignItems: 'center',
  },
};

export const Modal = RenderModal as unknown as React.ForwardRefExoticComponent<
  ModalProps & React.RefAttributes<RNModal>
> &
  ExportComponent;

Modal.useModal = useModal;
Modal.Header = Header;
Modal.Content = Content;
Modal.Footer = Footer;
