import {
  createContext,
  ReactNode,
  useCallback,
  useContext,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
} from 'react'
import { Text } from '../Text/index.js'
import {
  FloatingFocusManager,
  FloatingOverlay,
  FloatingPortal,
  useFloating,
} from '@floating-ui/react'
import { useTransitionStyles } from '@floating-ui/react'
import { colors } from '../../utils/colors.js'
import { shadows } from '../../utils/shadows.js'
import { radius } from '../../utils/radius.js'
import { IconButton } from '../IconButton/index.js'

const ModalContext = createContext<{ close: () => void }>({ close: () => {} })

type ModalRootProps = {
  children: ReactNode
  open: boolean
  onOpenChange: (open: boolean) => void
  size?: 'small' | 'regular' | 'large'
}
function Modal({
  children,
  open,
  onOpenChange,
  size = 'regular',
}: ModalRootProps) {
  const { context, refs } = useFloating({ open, onOpenChange })

  const { isMounted, styles } = useTransitionStyles(context, {
    duration: 250,
    initial: {
      opacity: 0,
      transform: 'scale(0.92)',
      transitionTimingFunction: 'ease-out',
    },
    close: {
      opacity: 0,
      transform: 'scale(0.92)',
      transitionTimingFunction: 'ease-in',
    },
  })
  const { styles: overlayStyles } = useTransitionStyles(context, {
    duration: 250,
    initial: {
      opacity: 0,
      transitionTimingFunction: 'ease-out',
    },
    close: {
      opacity: 0,
      transitionTimingFunction: 'ease-in',
    },
  })
  const close = useCallback(() => {
    onOpenChange(false)
  }, [])

  if (!isMounted) return null

  return (
    <ModalContext.Provider value={{ close }}>
      <FloatingPortal>
        <FloatingOverlay
          lockScroll
          style={{
            background: colors.black60,
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            zIndex: 4,
            padding: 24,
            ...overlayStyles,
          }}
        >
          <FloatingFocusManager context={context}>
            <div
              ref={refs.setFloating}
              style={{
                position: 'relative',
                width: {
                  small: 480,
                  regular: 840,
                  large: '100%',
                }[size],
                borderRadius: radius[24],
                background: colors.neutralInverted100,
                boxShadow: shadows.popoverLarge,
                outline: 'none',
                maxHeight: '100%',
                display: 'flex',
                flexDirection: 'column',
                ...styles,
              }}
            >
              <div
                style={{
                  background: colors.neutral10Background,
                  position: 'absolute',
                  inset: 0,
                  borderRadius: radius[24],
                  border: `1px solid ${colors.neutral10}`,
                  pointerEvents: 'none',
                }}
              />
              {children}
            </div>
          </FloatingFocusManager>
        </FloatingOverlay>
      </FloatingPortal>
    </ModalContext.Provider>
  )
}

type ModalHeaderProps = {
  children?: ReactNode
  title?: string
  description?: string
}
function ModalHeader({ children, title, description }: ModalHeaderProps) {
  const { close } = useContext(ModalContext)
  if (children) {
    return <div style={{ padding: '24px' }}>{children}</div>
  }

  return (
    <div
      style={{
        padding: '24px',
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
      }}
    >
      <div
        style={{
          display: 'flex',
          flexDirection: 'column',
          gap: 8,
          position: 'relative',
        }}
      >
        <Text variant="heading-bold" color="neutral100">
          {title}
        </Text>
        {description && (
          <Text variant="display-regular" color="neutral60">
            {description}
          </Text>
        )}
      </div>
      <div
        style={{
          display: 'flex',
          alignItems: 'flex-start',
          margin: -6,
        }}
      >
        <IconButton icon="close" onClick={close} />
      </div>
    </div>
  )
}

type ModalBodyProps = {
  children: ReactNode
}
function ModalBody({ children }: ModalBodyProps) {
  const ref = useRef<HTMLDivElement>(null)
  const [showBorders, setShowBorders] = useState(false)

  useLayoutEffect(() => {
    if (!ref.current) return

    const observer = new ResizeObserver(() => {
      setShowBorders(ref.current.scrollHeight > ref.current.clientHeight)
    })

    observer.observe(ref.current)

    return () => {
      observer.disconnect()
    }
  }, [])

  return (
    <div
      ref={ref}
      style={{
        flex: 1,
        overflowY: 'auto',
        scrollbarWidth: 'none',
        paddingLeft: 24,
        paddingRight: 24,
        paddingTop: 4,
        paddingBottom: 4,
        marginTop: -4,
        marginBottom: -4,
        zIndex: 4,
        ...(showBorders && {
          borderTop: `1px solid ${colors.neutral20Adjusted}`,
          borderBottom: `1px solid ${colors.neutral20Adjusted}`,
          paddingTop: 24,
          paddingBottom: 24,
          marginTop: 0,
          marginBottom: 0,
        }),
      }}
    >
      {children}
    </div>
  )
}

type ModalActionsProps = {
  children?: ReactNode | (({ close }: { close: () => void }) => ReactNode)
}
function ModalActions({ children }: ModalActionsProps) {
  const { close } = useContext(ModalContext)

  return (
    <div
      style={{
        display: 'flex',
        justifyContent: 'end',
        alignItems: 'center',
        gap: 16,
        padding: 24,
      }}
    >
      {typeof children === 'function' ? children({ close }) : children}
    </div>
  )
}

Modal.Header = ModalHeader
Modal.Actions = ModalActions
Modal.Body = ModalBody

export { Modal }
export type {
  ModalRootProps,
  ModalActionsProps,
  ModalBodyProps,
  ModalHeaderProps,
}
