"use client"

import { useCallback, useEffect, useRef, useState } from "react"
import { FocusScope, useOverlay } from "react-aria"
import { Button as ButtonRAC } from "react-aria-components"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts"

import { useScrollLock } from "@scandic-hotels/common/hooks/useScrollLock"
import { Typography } from "@scandic-hotels/design-system/Typography"

import ValidationError from "./ValidationError/index"
import PickerForm from "./Form"

import styles from "./guests-rooms-picker.module.css"

import type { GuestsRoom } from ".."
import type { BookingWidgetSchema } from "../Client"

export default function GuestsRoomsPickerForm({
  ariaLabelledBy,
}: {
  ariaLabelledBy?: string
}) {
  const rooms = useWatch<BookingWidgetSchema, "rooms">({ name: "rooms" })

  const checkIsDesktop = useMediaQuery("(min-width: 1367px)")
  const [isDesktop, setIsDesktop] = useState(true)
  const [isOpen, setIsOpen] = useState(false)
  const [containerConstraint, setContainerConstraint] = useState(0)
  const [showErrorModal, setShowErrorModal] = useState(false)

  const ref = useRef<HTMLDivElement | null>(null)
  // const childCount = rooms[0]?.childrenInRoom.length ?? 0 // ToDo Update for multiroom later
  const {
    clearErrors,
    formState: { errors },
  } = useFormContext()

  const [scrollPosition, setScrollPosition] = useState(0)
  const roomError = errors["rooms"]
  const { lockScroll, unlockScroll } = useScrollLock({
    autoLock: false,
  })
  useEffect(() => {
    if (roomError) {
      setShowErrorModal(true)
    }
  }, [roomError])

  useEffect(() => {
    clearErrors("rooms")
  }, [clearErrors])

  const timeoutRef = useRef<NodeJS.Timeout | null>(null)

  useEffect(() => {
    if (!roomError) return
    if (timeoutRef.current) return
    if (roomError) {
      timeoutRef.current = setTimeout(() => {
        setShowErrorModal(false)
        // magic number originates from animation
        // 5000ms delay + 120ms exectuion
        timeoutRef.current = null
      }, 5120)
    }

    return () => {}
  }, [clearErrors, roomError])

  useEffect(() => {
    setIsDesktop(checkIsDesktop)
  }, [checkIsDesktop])

  const updateHeight = useCallback((containerConstraint: number) => {
    // Get available space for picker to show without going beyond screen
    const bookingWidget = document.getElementById("booking-widget")
    const popoverElement = document.getElementById("guestsPopover")
    const maxHeight =
      window.innerHeight -
      (bookingWidget?.getBoundingClientRect().bottom ?? 0) -
      50
    const innerContainerHeight = popoverElement?.getBoundingClientRect().height

    const shouldAdjustHeight = Boolean(
      // height should be constrained
      maxHeight != containerConstraint &&
      innerContainerHeight &&
      maxHeight <= innerContainerHeight
    )
    const hasExcessVerticalSpace = Boolean(
      // no need to constrain height
      containerConstraint &&
      innerContainerHeight &&
      Math.floor(maxHeight) > Math.floor(innerContainerHeight)
    )
    if (shouldAdjustHeight) {
      // avoid clipping if there's only one room
      setContainerConstraint(Math.max(200, maxHeight))
    } else if (hasExcessVerticalSpace) {
      setContainerConstraint(0)
    }
  }, [])

  useEffect(() => {
    if (isDesktop && rooms.length > 1) {
      updateHeight(containerConstraint)
    }
  }, [
    isOpen,
    scrollPosition,
    isDesktop,
    updateHeight,
    containerConstraint,
    rooms,
  ])

  useEffect(() => {
    if (isOpen && isDesktop) {
      const handleScroll = () => {
        setScrollPosition(window.scrollY)
      }

      window.addEventListener("scroll", handleScroll)

      return () => {
        window.removeEventListener("scroll", handleScroll)
      }
    }
  }, [isOpen, isDesktop, rooms])

  const { overlayProps, underlayProps } = useOverlay(
    {
      isOpen,
      onClose: () => {
        setIsOpen(false)
        unlockScroll()
      },
      isDismissable: !errors.rooms,
    },
    ref
  )

  return (
    <>
      <div className={styles.container}>
        <Trigger
          rooms={rooms}
          onPress={() => {
            setIsOpen((prev) => !prev)
            if (!isDesktop && !isOpen) {
              lockScroll()
            } else {
              unlockScroll()
            }
          }}
          ariaLabelledBy={ariaLabelledBy}
        />

        {isOpen && (
          <div {...underlayProps}>
            <FocusScope contain restoreFocus autoFocus>
              <div
                id="guestsPopover"
                {...overlayProps}
                ref={ref}
                className={styles.pickerContainer}
                data-pressed={isOpen}
                data-rooms-open={isOpen}
                style={
                  isDesktop
                    ? containerConstraint > 0
                      ? { maxHeight: containerConstraint }
                      : { maxHeight: "none" }
                    : undefined
                }
              >
                <PickerForm
                  rooms={rooms}
                  onClose={() => {
                    setIsOpen((prev) => !prev)
                    unlockScroll()
                  }}
                />
              </div>
            </FocusScope>
          </div>
        )}
      </div>
      {showErrorModal && !isOpen && errors.rooms && <ValidationError />}
    </>
  )
}

function Trigger({
  rooms,
  onPress,
  ariaLabelledBy,
}: {
  rooms: GuestsRoom[]
  onPress?: () => void
  ariaLabelledBy?: string
}) {
  const intl = useIntl()

  const parts = [
    intl.formatMessage(
      {
        id: "booking.numberOfRooms",
        defaultMessage: "{totalRooms, plural, one {# room} other {# rooms}}",
      },
      { totalRooms: rooms.length }
    ),
    intl.formatMessage(
      {
        id: "booking.numberOfAdults",
        defaultMessage: "{adults, plural, one {# adult} other {# adults}}",
      },
      { adults: rooms.reduce((acc, room) => acc + room.adults, 0) }
    ),
  ]

  if (rooms.some((room) => room.childrenInRoom.length > 0)) {
    parts.push(
      intl.formatMessage(
        {
          id: "booking.numberOfChildren",
          defaultMessage:
            "{children, plural, one {# child} other {# children}}",
        },
        {
          children: rooms.reduce(
            (acc, room) => acc + room.childrenInRoom.length,
            0
          ),
        }
      )
    )
  }

  return (
    <Typography variant="Body/Paragraph/mdRegular">
      <ButtonRAC
        className={styles.triggerButton}
        type="button"
        onPress={onPress}
        aria-labelledby={ariaLabelledBy}
      >
        {parts.join(", ")}
      </ButtonRAC>
    </Typography>
  )
}
