import React from 'react'
import { View } from '../View'
import { Scroll } from '../Scroll'
import { TypeGuards } from '@codeleap/types'
import { Backdrop } from '../Backdrop'
import { useBackButton } from '../../utils/hooks'
import { Text } from '../Text'
import { Touchable } from '../Touchable'
import { ModalHeaderProps, ModalProps } from './types'
import { AnyRecord, AppIcon, useNestedStylesByKey, IJSX, StyledComponentProps } from '@codeleap/styles'
import { MobileStyleRegistry } from '../../Registry'
import { useStylesFor } from '../../hooks'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import { Platform, Modal as RNModal } from 'react-native'
import { FullWindowOverlay } from 'react-native-screens'
import { Button } from '../Button'

export * from './styles'
export * from './types'

/**
 * On iOS, React Native's `Modal` renders into a separate UIWindow that sits below the system keyboard, causing keyboard-aware layouts to break inside modals.
 * `FullWindowOverlay` from react-native-screens renders at the top of the main UIWindow's view hierarchy instead, which the keyboard responder chain can reach.
 * Android does not have this issue, so the standard `RNModal` is used there.
 */
const Overlay = Platform.select({
  ios: FullWindowOverlay,
  android: RNModal as any,
})

const DefaultHeader = (props: ModalHeaderProps) => {
  const {
    styles,
    buttonStyles,
    title = null,
    showClose = false,
    description = null,
    closable, debugName,
    closeIconName = 'x',
    toggle,
  } = props

  return <>
    {(title || showClose || description) && (
      <View style={styles.header}>
        <View style={styles.titleWrapper}>
          {TypeGuards.isString(title) ? (
            <Text text={title} style={styles.title} />
          ) : (
            title
          )}

          {(showClose && closable) ? (
            <Button
              debugName={`${debugName} modal close button`}
              icon={closeIconName as AppIcon}
              onPress={toggle}
              style={buttonStyles}
            />
          ) : null}
        </View>

        {TypeGuards.isString(description) ? (
          <Text text={description} style={styles.description} />
        ) : (
          description
        )}
      </View>
    )}
  </>
}

export const Modal = (modalProps: ModalProps) => {
  const {
    visible,
    closable,
    footer,
    children,
    toggle = () => null,
    dismissOnBackdrop,
    header,
    debugName,
    scroll,
    renderHeader: Header,
    scrollProps = {},
    closeOnHardwareBackPress,
    style,
    boxExiting,
    boxEntering,
    withPortal,
    ...props
  } = {
    ...Modal.defaultProps,
    ...modalProps,
  }

  const styles = useStylesFor(Modal.styleRegistryName, style)

  const buttonStyles = useNestedStylesByKey('closeButton', styles)

  const ScrollComponent = scroll ? Scroll : View
  const scrollStyle = scroll ? styles?.scroll : styles?.innerWrapper

  /** Returning `true` from the back-button handler is required to signal to Android that the event was consumed; without it the OS will still perform the default back navigation. */
  useBackButton(() => {
    if (visible && closeOnHardwareBackPress) {
      toggle()
      return true
    }
  }, [visible, toggle, closeOnHardwareBackPress])

  return (
    <Overlay transparent visible={visible}>
      <View style={styles?.wrapper} pointerEvents={visible ? 'auto' : 'none'}>
        <Backdrop
          visible={visible}
          debugName={`Modal ${debugName} backdrop`}
          style={{
            'wrapper:hidden': styles['backdrop:hidden'],
            'wrapper:visible': styles['backdrop:visible'],
            'wrapper': styles?.backdrop,
          }}
          wrapperProps={{
            // @ts-expect-error
            transition: styles['backdrop:transition'],
          }}
        />

        <ScrollComponent
          contentContainerStyle={styles?.scrollContent}
          showsVerticalScrollIndicator={false}
          keyboardAware
          animated
          {...scrollProps}
          style={scrollStyle}
        >
          {dismissOnBackdrop ? (
            <Touchable
              onPress={closable && visible ? toggle : (() => { })}
              debounce={400}
              debugName={'Modal backdrop touchable'}
              style={styles?.backdropTouchable}
              android_ripple={null}
              noFeedback
            />) : null}
          {
            visible ? (
              <View
                animated
                // @ts-ignore
                entering={boxEntering}
                exiting={boxExiting}
                {...props}
                style={styles?.box}
              >
                {header ? header : (
                  <Header
                    {...modalProps}
                    closable={closable}
                    styles={styles}
                    buttonStyles={buttonStyles}
                  />
                )}

                <View style={styles?.body}>{children}</View>

                {footer ? (
                  <View style={styles?.footer}>
                    {typeof footer === 'string' ? <Text text={footer} /> : footer}
                  </View>
                ) : null}
              </View>
            ) : null
          }
        </ScrollComponent>
      </View>
    </Overlay>
  )
}

Modal.styleRegistryName = 'Modal'
Modal.elements = ['wrapper', 'box', 'backdrop', 'innerWrapper', 'scroll', 'body', 'footer', 'header', 'title', 'description', 'closeButton']
Modal.rootElement = 'wrapper'

Modal.withVariantTypes = <S extends AnyRecord>(styles: S) => {
  return Modal as (props: StyledComponentProps<ModalProps, typeof styles>) => IJSX
}

Modal.defaultProps = {
  closeIconName: 'close' as AppIcon,
  closable: true,
  dismissOnBackdrop: true,
  scroll: true,
  closeOnHardwareBackPress: true,
  boxEntering: FadeIn.duration(100).delay(50).build(),
  boxExiting: FadeOut.duration(100).build(),
  withPortal: true,
  renderHeader: DefaultHeader,
} as Partial<ModalProps>

MobileStyleRegistry.registerComponent(Modal)
