'use client'

import { cx } from 'class-variance-authority'
import { AnimatePresence, motion } from 'motion/react'
import { type PropsWithChildren, useEffect, useState } from 'react'
import {
  Dialog,
  DialogTrigger,
  Modal as AriaModal,
  ModalOverlay,
} from 'react-aria-components'
import { useIntl } from 'react-intl'

import { MaterialIcon } from '../Icons/MaterialIcon'
import Subtitle from '../Subtitle'

import {
  type AnimationState,
  AnimationStateEnum,
  type InnerModalProps,
  type ModalProps,
} from './modal'
import { fade, slideInOut } from './motionVariants'
import { modalContentVariants } from './variants'

import styles from './modal.module.css'
import { Typography } from '../Typography'

const MotionOverlay = motion.create(ModalOverlay)
const MotionModal = motion.create(AriaModal)

function InnerModal({
  animation,
  onAnimationComplete = () => undefined,
  setAnimation,
  onToggle,
  isOpen,
  children,
  title,
  subtitle,
  withActions,
  hideHeader,
  className,
}: PropsWithChildren<InnerModalProps>) {
  const intl = useIntl()

  const contentClassNames = modalContentVariants({
    withActions: withActions,
  })

  function modalStateHandler(newAnimationState: AnimationState) {
    setAnimation((currentAnimationState) =>
      newAnimationState === AnimationStateEnum.hidden &&
      currentAnimationState === AnimationStateEnum.hidden
        ? AnimationStateEnum.unmounted
        : currentAnimationState
    )
    if (newAnimationState === AnimationStateEnum.visible) {
      onAnimationComplete()
    }
  }

  function onOpenChange(state: boolean) {
    onToggle!(state)
  }

  return (
    <MotionOverlay
      animate={animation}
      className={styles.overlay}
      initial="hidden"
      isDismissable
      // TODO: Enabling this causes the modal to never unmount.
      // Seems to be an issue with react-aria-components, see https://github.com/adobe/react-spectrum/issues/7563.
      // Exit animations can probably be fixed by rewriting to a solution similar to
      // https://react-spectrum.adobe.com/react-aria/examples/framer-modal-sheet.html.
      // isExiting={animation === AnimationStateEnum.hidden}
      onAnimationComplete={modalStateHandler}
      variants={fade}
      isOpen={isOpen}
      onOpenChange={onOpenChange}
    >
      <MotionModal
        className={cx(styles.modal, className)}
        variants={slideInOut}
        animate={animation}
        initial="hidden"
      >
        <Dialog
          className={styles.dialog}
          aria-label={intl.formatMessage({
            id: 'modal.dialog',
            defaultMessage: 'Dialog',
          })}
        >
          {({ close }) => (
            <>
              {!hideHeader && (
                <header
                  className={`${styles.header} ${!subtitle ? styles.verticalCenter : ''}`}
                >
                  <div>
                    {title && (
                      <Subtitle type="one" color="uiTextHighContrast">
                        {title}
                      </Subtitle>
                    )}
                    {subtitle && (
                      <Typography variant="Body/Lead text">
                        <span>{subtitle}</span>
                      </Typography>
                    )}
                  </div>

                  <button
                    onClick={close}
                    type="button"
                    className={styles.close}
                  >
                    <MaterialIcon icon="close" color="Icon/Feedback/Neutral" />
                  </button>
                </header>
              )}

              <div className={contentClassNames}>{children}</div>
            </>
          )}
        </Dialog>
      </MotionModal>
    </MotionOverlay>
  )
}

export default function Modal({
  onAnimationComplete = () => undefined,
  trigger,
  isOpen,
  onToggle,
  onOpenChange,
  title,
  subtitle,
  children,
  withActions = false,
  hideHeader = false,
  className = '',
}: PropsWithChildren<ModalProps>) {
  const [animation, setAnimation] = useState<AnimationState>(
    AnimationStateEnum.visible
  )

  useEffect(() => {
    if (typeof isOpen === 'boolean') {
      setAnimation(
        isOpen ? AnimationStateEnum.visible : AnimationStateEnum.hidden
      )
    }
    if (isOpen === undefined) {
      setAnimation(AnimationStateEnum.unmounted)
    }
  }, [isOpen])

  const shouldRender = isOpen || animation !== AnimationStateEnum.unmounted

  if (!trigger) {
    return (
      <AnimatePresence>
        {shouldRender && (
          <InnerModal
            onAnimationComplete={onAnimationComplete}
            animation={animation}
            setAnimation={setAnimation}
            onToggle={onToggle}
            isOpen={isOpen}
            title={title}
            subtitle={subtitle}
            withActions={withActions}
            hideHeader={hideHeader}
            className={className}
          >
            {children}
          </InnerModal>
        )}
      </AnimatePresence>
    )
  }

  return (
    <DialogTrigger
      onOpenChange={(isOpen) => {
        setAnimation(
          isOpen ? AnimationStateEnum.visible : AnimationStateEnum.hidden
        )
        onOpenChange?.(isOpen)
      }}
    >
      {trigger}
      <AnimatePresence>
        {shouldRender && (
          <InnerModal
            onAnimationComplete={onAnimationComplete}
            animation={animation}
            setAnimation={setAnimation}
            title={title}
            subtitle={subtitle}
            withActions={withActions}
            className={className}
          >
            {children}
          </InnerModal>
        )}
      </AnimatePresence>
    </DialogTrigger>
  )
}
