"use client"
import * as ModalPrimitive from "@radix-ui/react-dialog"
import useMergedRef from "@react-hook/merged-ref"
import { motion } from "motion/react"
import React, {
  ComponentProps,
  ComponentPropsWithoutRef,
  ElementRef,
  forwardRef,
  useCallback,
  useRef,
  useState,
} from "react"
import { tv, type VariantProps } from "tailwind-variants"
import { useCallbackRef } from "../../hooks"
import { Close } from "../../icons/material-symbols/outlined/Close"
import { overlayVariants } from "../../styles/utils/overlay"
import { customScrollbarClassname } from "../../styles/utils/scrollbar"
import { classNames } from "../../utils"
import { AnimateHeight } from "../AnimateHeight"
import { Block, BlockProps } from "../Block"
import {
  BottomSheet,
  BottomSheetBody,
  BottomSheetFooter,
  BottomSheetFooterButton,
  BottomSheetHeader,
  BottomSheetHeaderTitle,
} from "../BottomSheet"
import { Button, ButtonProps } from "../Button"
import { FlexCenter, FlexCenterProps } from "../FlexCenter"
import { FlexColumn } from "../FlexColumn"
import { Media, useIsLessThanMd } from "../Media"
import { Text } from "../Text"

export type ModalProps = Pick<
  ComponentProps<typeof ModalPrimitive.Root>,
  "open" | "defaultOpen" | "onOpenChange" | "modal"
> &
  VariantProps<typeof modalVariants> &
  Omit<ComponentProps<typeof ModalPrimitive.Content>, "content"> & {
    content: React.ReactNode
    withCloseIcon?: boolean
    overrides?: {
      Overlay?: Partial<ComponentProps<typeof ModalPrimitive.Overlay>>
      CloseButton?: {
        className?: string
      }
    }
  }

const modalVariants = tv({
  base: classNames(
    "z-modal rounded-xl border border-border-1 ease-out-quint relative bg-bg-app",
    "max-w-[calc(100vw-32px)]",
    "data-[state=open]:animate-in data-[state=closed]:animate-out",
    "data-[state=closed]:duration-300 data-[state=open]:duration-300",
    "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
    "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
    "focus-visible:outline-hidden",
  ),
  variants: {
    size: {
      sm: "w-[420px]",
      lg: "w-[550px]",
    },
  },
})

const MODAL_CONTENT_SELECTOR = "[data-id='ModalContent']"
const MODAL_FOOTER_SELECTOR = "[data-id='ModalFooter']"

const ModalBase = forwardRef<
  ElementRef<typeof ModalPrimitive.Content>,
  ModalProps
>(function Modal(
  {
    open,
    defaultOpen,
    onOpenChange,
    content,
    children,
    className,
    modal,
    withCloseIcon = true,
    overrides,
    size = "lg",
    ...props
  },
  ref,
) {
  // We need to keep track of the open state internally so we can render the bottom sheet with the correct state on mobile
  const [openedState, setOpenedState] = useState(defaultOpen ?? open)
  const resolvedOpenState = open === undefined ? openedState : open
  // This is needed due to a bug seemingly in vaul where unmounting the component through Media still locks the cursor.
  const isLessThanMd = useIsLessThanMd()
  const triggerRef = useRef<HTMLButtonElement>(null)
  const handleOnOpenChange = useCallback(
    (open: boolean) => {
      setOpenedState(open)
      onOpenChange?.(open)
    },
    [onOpenChange],
  )

  return (
    <>
      <Media lessThan="md">
        <BottomSheet
          className={className}
          content={content}
          modal={modal}
          open={isLessThanMd && resolvedOpenState}
          overrides={overrides}
          withCloseIcon={withCloseIcon}
          onOpenChange={handleOnOpenChange}
        >
          {children}
        </BottomSheet>
      </Media>
      <Media greaterThanOrEqual="md">
        <ModalPrimitive.Root
          defaultOpen={defaultOpen}
          modal={modal}
          open={!isLessThanMd && resolvedOpenState}
          onOpenChange={handleOnOpenChange}
        >
          <ModalPrimitive.Trigger asChild ref={triggerRef}>
            {children}
          </ModalPrimitive.Trigger>
          <ModalPrimitive.Portal>
            <ModalPrimitive.Overlay
              data-testid="Overlay"
              {...overrides?.Overlay}
              className={classNames(
                overlayVariants(),
                "z-modal flex items-center justify-center",
                overrides?.Overlay?.className,
              )}
            >
              <ModalPrimitive.Content
                aria-describedby={undefined}
                className={classNames(modalVariants({ size }), className)}
                data-id="ModalContent"
                ref={ref}
                {...props}
              >
                <AnimateHeight>
                  <FlexColumn className="max-h-[calc(100vh-64px)]">
                    {content}
                  </FlexColumn>
                </AnimateHeight>

                {withCloseIcon && (
                  <ModalPrimitive.Close
                    className={classNames(
                      "absolute right-6 top-6 z-modal",
                      overrides?.CloseButton?.className,
                    )}
                  >
                    <Close fill="interactive-primary-styles" />
                  </ModalPrimitive.Close>
                )}
              </ModalPrimitive.Content>
            </ModalPrimitive.Overlay>
          </ModalPrimitive.Portal>
        </ModalPrimitive.Root>
      </Media>
    </>
  )
})

const modalHeaderVariants = tv({
  base: "px-6 pb-6",
  variants: {
    variant: {
      default: "pt-6",
      heroMedia: "flex-col justify-center pt-12",
    },
  },
})

export const ModalHeader = ({
  className,
  variant = "default",
  ...props
}: FlexCenterProps & VariantProps<typeof modalHeaderVariants>) => {
  return (
    <>
      <Media lessThan="md">
        <BottomSheetHeader className={className} variant={variant} {...props} />
      </Media>
      <Media greaterThanOrEqual="md">
        <FlexCenter
          className={classNames(
            modalHeaderVariants({ variant }),

            className,
          )}
          {...props}
        />
      </Media>
    </>
  )
}

export const ModalHeaderTitle = forwardRef<
  ElementRef<typeof ModalPrimitive.Title>,
  ComponentPropsWithoutRef<typeof ModalPrimitive.Title>
>(function ModalHeaderTitle({ className, children, ...props }, ref) {
  return (
    <>
      <Media lessThan="md">
        <BottomSheetHeaderTitle className={className} {...props}>
          {children}
        </BottomSheetHeaderTitle>
      </Media>
      <Media greaterThanOrEqual="md">
        <ModalPrimitive.Title
          asChild
          className={className}
          ref={ref}
          {...props}
        >
          <Text.Display asChild size="sm">
            <h4>{children}</h4>
          </Text.Display>
        </ModalPrimitive.Title>
      </Media>
    </>
  )
})

export const ModalBody = forwardRef<ElementRef<typeof Block>, BlockProps>(
  function ModalBody({ className, children, ...props }, ref) {
    const [bodyRef, setBodyRef] = useCallbackRef<HTMLDivElement>()
    const mergedRef = useMergedRef(ref, setBodyRef)
    const hasFooter = Boolean(
      bodyRef?.current
        ?.closest(MODAL_CONTENT_SELECTOR)
        ?.querySelector(MODAL_FOOTER_SELECTOR),
    )

    return (
      <>
        <Media lessThan="md">
          <BottomSheetBody className={className} ref={ref} {...props}>
            {children}
          </BottomSheetBody>
        </Media>
        <Media greaterThanOrEqual="md">
          <Block
            className={classNames(
              "flex flex-col overflow-y-auto px-6",
              hasFooter ? "pb-6" : "pb-10",
              customScrollbarClassname,
              className,
            )}
            ref={mergedRef}
            {...props}
          >
            {children}
          </Block>
        </Media>
      </>
    )
  },
)

export const ModalFooter = forwardRef<
  ElementRef<typeof motion.div>,
  ComponentPropsWithoutRef<typeof motion.div>
>(function ModalFooter({ children, className, ...props }, ref) {
  return (
    <>
      <Media lessThan="md">
        <BottomSheetFooter className={className} ref={ref} {...props}>
          {children}
        </BottomSheetFooter>
      </Media>
      <Media greaterThanOrEqual="md">
        <motion.div
          className={classNames(
            "flex w-full gap-3 bg-bg-primary border-t border-border-1 p-4",
            className,
          )}
          data-id="ModalFooter"
          layout="position"
          ref={ref}
          transition={{ type: "spring", bounce: 0, duration: 0.3 }}
          {...props}
        >
          {children}
        </motion.div>
      </Media>
    </>
  )
})

export const ModalFooterButton = forwardRef<
  ElementRef<typeof Button>,
  ButtonProps
>(function ModalFooterButton({ className, ...rest }, ref) {
  return (
    <>
      <Media lessThan="md">
        <BottomSheetFooterButton className={className} ref={ref} {...rest} />
      </Media>
      <Media greaterThanOrEqual="md">
        <Button
          className={classNames("w-full", className)}
          ref={ref}
          {...rest}
        />
      </Media>
    </>
  )
})

/**
 * A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
 * Turns into a bottom sheet on mobile.
 */
export const Modal = Object.assign(ModalBase, {
  Header: Object.assign(ModalHeader, { Title: ModalHeaderTitle }),
  Body: ModalBody,
  Footer: Object.assign(ModalFooter, { Button: ModalFooterButton }),
})
