import { useCallback, useEffect, useRef, useState } from "react"
import { Dialog, DialogTrigger, Popover } from "react-aria-components"
import { type FieldError, useFormContext } from "react-hook-form"
import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts"

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

import { REDEMPTION } from "@/constants/booking"

import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button"
import Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Input from "@/components/TempDesignSystem/Form/Input"
import { getErrorMessage } from "@/components/TempDesignSystem/Form/Input/errors"
import Switch from "@/components/TempDesignSystem/Form/Switch"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"

import { bookingWidgetErrors } from "../../schema"
import { Input as BookingWidgetInput } from "../Input"

import styles from "./booking-code.module.css"

import type {
  BookingCodeSchema,
  BookingWidgetSchema,
} from "@/types/components/bookingWidget"
import type { ButtonProps } from "@/components/TempDesignSystem/Button/button"

export default function BookingCode() {
  const intl = useIntl()
  const checkIsTablet = useMediaQuery(
    "(min-width: 768px) and (max-width: 1366px)"
  )
  const checkIsDesktop = useMediaQuery("(min-width: 1367px)")
  const [isTablet, setIsTablet] = useState(false)
  const [isDesktop, setIsDesktop] = useState(false)
  const {
    setValue,
    formState: { errors },
    getValues,
    trigger,
  } = useFormContext<BookingWidgetSchema>()

  const bookingCode: BookingCodeSchema = getValues("bookingCode")
  const [showRemember, setShowRemember] = useState(false)
  const [showRememberMobile, setShowRememberMobile] = useState(false)
  const codeError = errors["bookingCode"]?.value
  const codeVoucher = intl.formatMessage({
    defaultMessage: "Code / Voucher",
  })
  const addCode = intl.formatMessage({
    defaultMessage: "Add code",
  })
  const ref = useRef<HTMLDivElement | null>(null)
  const removeExtraRoomsText = intl.formatMessage({
    defaultMessage: "Remove extra rooms",
  })

  function updateBookingCodeFormValue(value: string) {
    // Set value and show error if validation fails
    setValue("bookingCode.value", value.toUpperCase(), {
      shouldValidate: true,
      shouldDirty: true,
    })

    if (getValues(REDEMPTION)) {
      // Remove the redemption as user types booking code and show notification for the same
      // Add delay to handle table mode rendering
      setTimeout(function () {
        setValue(REDEMPTION, false, {
          shouldDirty: true,
        })
      })
      // Hide the above notification popup after 5 seconds by re-triggering validation
      // This is kept consistent with location search field error notification timeout
      setTimeout(function () {
        trigger("bookingCode.value")
      }, 5000)
    }
  }

  const closeIfOutside = useCallback(
    (target: HTMLElement) => {
      if (
        ref.current &&
        target &&
        !ref.current.contains(target) &&
        // This is for mobile layout having "Remove extra rooms" button outside the container
        target.innerText !== removeExtraRoomsText
      ) {
        setShowRemember(false)
        if (codeError) {
          setValue("bookingCode.value", "", {
            shouldValidate: true,
            shouldDirty: true,
          })
        }
      }
    },
    [setShowRemember, setValue, removeExtraRoomsText, ref, codeError]
  )

  function showRememberCheck() {
    setShowRemember(true)
  }

  useEffect(() => {
    setIsTablet(checkIsTablet)
  }, [checkIsTablet])

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

  const isRememberMobileVisible =
    !isDesktop && (showRemember || !!bookingCode?.remember)
  useEffect(() => {
    setShowRememberMobile(isRememberMobileVisible)
  }, [isRememberMobileVisible])

  useEffect(() => {
    function handleClickOutside(evt: Event) {
      if (showRemember) {
        const target = evt.target as HTMLElement
        closeIfOutside(target)
      }
    }
    document.body.addEventListener("click", handleClickOutside)
    return () => {
      document.body.removeEventListener("click", handleClickOutside)
    }
  }, [closeIfOutside, showRemember])

  return isTablet ? (
    <TabletBookingCode
      bookingCode={bookingCode}
      updateValue={updateBookingCodeFormValue}
    />
  ) : (
    <div
      className={styles.container}
      ref={ref}
      onFocus={showRememberCheck}
      onBlur={(e) => closeIfOutside(e.nativeEvent.relatedTarget as HTMLElement)}
    >
      <div className={styles.bookingCode}>
        <div className={styles.bookingCodeLabel}>
          <Caption
            color={showRemember ? "uiTextActive" : "red"}
            type="bold"
            asChild
          >
            <span>{codeVoucher}</span>
          </Caption>
          <CodeRulesModal />
        </div>
        <BookingWidgetInput
          className="input"
          type="search"
          placeholder={addCode}
          name="bookingCode.value"
          id="booking-code"
          onChange={(event) => updateBookingCodeFormValue(event.target.value)}
          autoComplete="off"
          value={bookingCode?.value}
        />
      </div>
      {isDesktop ? (
        <div
          className={
            showRemember
              ? styles.bookingCodeRememberVisible
              : styles.bookingCodeRemember
          }
        >
          {codeError?.message ? (
            <BookingCodeError codeError={codeError} />
          ) : (
            <CodeRemember
              bookingCodeValue={bookingCode?.value}
              onApplyClick={() => setShowRemember(false)}
            />
          )}
        </div>
      ) : (
        <>
          {codeError?.message ? (
            <BookingCodeError codeError={codeError} />
          ) : (
            <div
              className={
                showRememberMobile
                  ? styles.bookingCodeRememberVisible
                  : styles.bookingCodeRemember
              }
            >
              <Switch name="bookingCode.remember" className="mobile-switch">
                <Caption asChild>
                  <span>
                    {intl.formatMessage({
                      defaultMessage: "Remember code",
                    })}
                  </span>
                </Caption>
              </Switch>
            </div>
          )}
        </>
      )}
    </div>
  )
}

type CodeRememberProps = {
  bookingCodeValue: string | undefined
  onApplyClick: () => void
}

function CodeRulesModal() {
  const intl = useIntl()
  const codeVoucher = intl.formatMessage({
    defaultMessage: "Code / Voucher",
  })
  const bookingCodeTooltipText = intl.formatMessage({
    defaultMessage:
      "If you're booking a promotional offer or a Corporate negotiated rate you'll need a special booking code. Don't use any special characters such as (.) (,) (-) (:). If you would like to make a booking with code VOF, please call us +46 8 517 517 20.Save your booking code for the next time you visit the page by ticking the box “Remember”. Don't tick the box if you're using a public computer to avoid unauthorized access to your booking code.",
  })

  return (
    <Modal
      trigger={
        <Button intent="text">
          <MaterialIcon
            icon="info"
            color="Icon/Interactive/Placeholder"
            size={20}
          />
        </Button>
      }
      title={codeVoucher}
    >
      <Body color="uiTextHighContrast" className={styles.bookingCodeTooltip}>
        {bookingCodeTooltipText}
      </Body>
    </Modal>
  )
}

function CodeRemember({ bookingCodeValue, onApplyClick }: CodeRememberProps) {
  const intl = useIntl()

  return (
    <>
      <Checkbox name="bookingCode.remember">
        <Caption asChild>
          <span>
            {intl.formatMessage({
              defaultMessage: "Remember code",
            })}
          </span>
        </Caption>
      </Checkbox>
      {bookingCodeValue ? (
        <Button
          size="small"
          className={styles.hideOnMobile}
          intent="secondary"
          type="button"
          onClick={onApplyClick}
        >
          {intl.formatMessage({
            defaultMessage: "Apply",
          })}
        </Button>
      ) : null}
    </>
  )
}

function BookingCodeError({ codeError }: { codeError: FieldError }) {
  const intl = useIntl()
  const isMultiroomErr =
    codeError.message ===
      bookingWidgetErrors.MULTIROOM_BOOKING_CODE_UNAVAILABLE ||
    codeError.message === bookingWidgetErrors.MULTIROOM_REWARD_NIGHT_UNAVAILABLE
  const isInvalidErr =
    codeError.message === bookingWidgetErrors.BOOKING_CODE_INVALID

  return (
    <div className={styles.errorContainer}>
      <Caption className={styles.error}>
        <MaterialIcon
          icon="error"
          color={
            isInvalidErr ? "Icon/Feedback/Information" : "Icon/Feedback/Error"
          }
          className={styles.errorIcon}
        />
        {getErrorMessage(intl, codeError.message)}
      </Caption>
      {isMultiroomErr ? (
        <RemoveExtraRooms className={styles.hideOnMobile} />
      ) : null}
    </div>
  )
}

export function RemoveExtraRooms({ ...props }: ButtonProps) {
  const intl = useIntl()
  const { getValues, setValue, trigger } = useFormContext<BookingWidgetSchema>()
  function removeExtraRooms() {
    // Timeout to delay the event scheduling issue with touch events on mobile
    window.setTimeout(() => {
      const rooms = getValues("rooms")[0]
      setValue("rooms", [rooms], { shouldValidate: true, shouldDirty: true })
      trigger("bookingCode.value")
      trigger(REDEMPTION)
    }, 300)
  }

  return (
    <Button
      type="button"
      onClick={removeExtraRooms}
      size="small"
      intent="primary"
      {...props}
    >
      {intl.formatMessage({
        defaultMessage: "Remove extra rooms",
      })}
    </Button>
  )
}

function TabletBookingCode({
  bookingCode,
  updateValue,
}: {
  bookingCode: BookingCodeSchema
  updateValue: (value: string) => void
}) {
  const intl = useIntl()
  const [isOpen, setIsOpen] = useState(!!bookingCode?.value)
  const {
    setValue,
    register,
    formState: { errors },
  } = useFormContext<BookingWidgetSchema>()
  const codeError = errors["bookingCode"]?.value
  const codeVoucher = intl.formatMessage({
    defaultMessage: "Code / Voucher",
  })

  function toggleModal(isOpen: boolean) {
    if (document.body) {
      if (isOpen) {
        document.body.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.
        document.body.style.overflow = "clip !important"
      }
    }
    if (!isOpen && !bookingCode?.value) {
      setValue("bookingCode.flag", false, { shouldDirty: true })
      setIsOpen(isOpen)
    } else if (!codeError || isOpen) {
      setIsOpen(isOpen)
      if (isOpen || bookingCode?.value) {
        setValue("bookingCode.flag", true, { shouldDirty: true })
      }
    }
  }

  return (
    <div className={styles.container}>
      <DialogTrigger isOpen={isOpen} onOpenChange={toggleModal}>
        <Button type="button" intent="text">
          {/* For some reason Checkbox click triggers twice modal state change, which returns the modal back to old state. So we are using overlay as trigger for modal */}
          <div className={styles.overlayTrigger}></div>
          <Checkbox
            checked={!!bookingCode?.value}
            {...register("bookingCode.flag", {
              onChange: function () {
                if (bookingCode?.value || isOpen) {
                  setValue("bookingCode.flag", true, { shouldDirty: true })
                }
              },
            })}
          >
            <Caption color="uiTextMediumContrast" type="bold" asChild>
              <span>{codeVoucher}</span>
            </Caption>
          </Checkbox>
        </Button>
        <Popover
          className={styles.codePopover}
          placement="bottom start"
          offset={36}
        >
          <Dialog>
            {({ close }) => (
              <div className={styles.popover}>
                <Input
                  label={intl.formatMessage({
                    defaultMessage: "Add code",
                  })}
                  {...register("bookingCode.value", {
                    onChange: (e) => updateValue(e.target.value),
                  })}
                  autoComplete="off"
                  hideError
                />
                <div className={styles.bookingCodeRememberVisible}>
                  {codeError?.message ? (
                    <BookingCodeError codeError={codeError} />
                  ) : (
                    <CodeRemember
                      bookingCodeValue={bookingCode?.value}
                      onApplyClick={close}
                    />
                  )}
                </div>
              </div>
            )}
          </Dialog>
        </Popover>
      </DialogTrigger>
      <CodeRulesModal />
    </div>
  )
}
