"use client"

import { useCallback, useEffect, useId, useState } from "react"
import {
  Button,
  Dialog,
  DialogTrigger,
  Modal,
  Popover,
} from "react-aria-components"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts"

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

import PickerForm from "./Form"

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

import type { BookingWidgetSchema } from "@/types/components/bookingWidget"
import type { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"

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

  const popoverId = useId()
  const checkIsDesktop = useMediaQuery("(min-width: 1367px)")
  const [isDesktop, setIsDesktop] = useState(true)
  const [isOpen, setIsOpen] = useState(false)
  const [containerHeight, setContainerHeight] = useState(0)
  const childCount = rooms[0]?.childrenInRoom.length ?? 0 // ToDo Update for multiroom later

  //isOpen is the 'old state', so isOpen === true means "The modal is open and WILL be closed".
  async function setOverflowClip(isOpen: boolean) {
    const bodyElement = document.body
    if (bodyElement) {
      if (isOpen) {
        bodyElement.style.overflow = "visible"
      } else {
        // !important needed to override 'overflow: hidden' set by react-aria.
        // 'overflow: hidden' does not work in combination with other sticky positioned elements, which clip does.
        bodyElement.style.overflow = "clip !important"
      }
    }
    if (!isOpen) {
      const state = await trigger("rooms")
      if (state) {
        setIsOpen(isOpen)
      }
    }
  }

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

  const updateHeight = useCallback(() => {
    // Get available space for picker to show without going beyond screen
    const bookingWidget = document.getElementById("booking-widget")
    let maxHeight =
      window.innerHeight -
      (bookingWidget?.getBoundingClientRect().bottom ?? 0) -
      50
    const innerContainerHeight = document
      .getElementsByClassName(popoverId)[0]
      ?.getBoundingClientRect().height
    if (
      maxHeight != containerHeight &&
      innerContainerHeight &&
      maxHeight <= innerContainerHeight
    ) {
      setContainerHeight(maxHeight)
    } else if (
      containerHeight &&
      innerContainerHeight &&
      maxHeight > innerContainerHeight
    ) {
      setContainerHeight(0)
    }
  }, [containerHeight, popoverId])

  useEffect(() => {
    if (isDesktop && rooms.length > 0) {
      updateHeight()
    }
  }, [childCount, isDesktop, updateHeight, rooms])

  return isDesktop ? (
    <DialogTrigger onOpenChange={setOverflowClip} isOpen={isOpen}>
      <Trigger
        rooms={rooms}
        className={styles.triggerDesktop}
        triggerFn={() => {
          setIsOpen(true)
        }}
      />
      <Popover
        className={popoverId}
        placement="bottom start"
        offset={36}
        style={containerHeight ? { overflow: "auto" } : undefined}
      >
        <Dialog className={styles.pickerContainerDesktop}>
          {({ close }) => <PickerForm rooms={rooms} onClose={close} />}
        </Dialog>
      </Popover>
    </DialogTrigger>
  ) : (
    <DialogTrigger onOpenChange={setOverflowClip} isOpen={isOpen}>
      <Trigger
        rooms={rooms}
        className={styles.triggerMobile}
        triggerFn={() => {
          setIsOpen(true)
        }}
        ariaLabelledBy={ariaLabelledBy}
      />
      <Modal>
        <Dialog className={styles.pickerContainerMobile}>
          {({ close }) => <PickerForm rooms={rooms} onClose={close} />}
        </Dialog>
      </Modal>
    </DialogTrigger>
  )
}

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

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

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

  return (
    <Button
      className={`${className} ${styles.btn}`}
      type="button"
      onPress={triggerFn}
      aria-labelledby={ariaLabelledBy}
    >
      <Typography variant="Body/Paragraph/mdRegular">
        <span>{parts.join(", ")}</span>
      </Typography>
    </Button>
  )
}
