"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"

import { dt } from "@/lib/dt"

import Body from "@/components/TempDesignSystem/Text/Body"
import useLang from "@/hooks/useLang"

import DatePickerRangeDesktop from "./Range/Desktop"
import DatePickerRangeMobile from "./Range/Mobile"

import styles from "./date-picker.module.css"

import type { DateRange } from "react-day-picker"

import type { DatePickerFormProps } from "@/types/components/datepicker"

export default function DatePickerForm({ name = "date" }: DatePickerFormProps) {
  const lang = useLang()
  const intl = useIntl()

  const [isOpen, setIsOpen] = useState(false)
  const selectedDate = useWatch({ name })
  const { register, setValue } = useFormContext()
  const ref = useRef<HTMLDivElement | null>(null)

  const close = useCallback(() => {
    if (!selectedDate.toDate) {
      setValue(
        name,
        {
          fromDate: selectedDate.fromDate,
          toDate: dt(selectedDate.fromDate).add(1, "day").format("YYYY-MM-DD"),
        },
        { shouldDirty: true }
      )
    }

    setIsOpen(false)
  }, [name, setValue, selectedDate])

  function showOnFocus() {
    setIsOpen(true)
  }

  function handleSelectDate(
    _nextRange: DateRange | undefined,
    selectedDay: Date
  ) {
    const now = dt()
    const dateClicked = dt(selectedDay)
    const dateClickedFormatted = dateClicked.format("YYYY-MM-DD")
    /* check if selected date is not before todays date,
     which happens when "Enter" key is pressed in any other input field of the form */
    if (!dateClicked.isBefore(now, "day")) {
      // Handle form value updates based on the requirements
      if (selectedDate.fromDate && selectedDate.toDate) {
        // Both dates were previously selected, starting fresh with new date
        setValue(
          name,
          {
            fromDate: dateClickedFormatted,
            toDate: undefined,
          },
          { shouldDirty: true }
        )
      } else if (selectedDate.fromDate && !selectedDate.toDate) {
        // If the selected day is the same as the first date, we don't need to update the form value
        if (dateClicked.isSame(selectedDate.fromDate)) {
          return
        }
        // We're selecting the second date
        if (dateClicked.isBefore(selectedDate.fromDate)) {
          // If second selected date is before first date, swap them
          setValue(
            name,
            {
              fromDate: dateClickedFormatted,
              toDate: selectedDate.fromDate,
            },
            { shouldDirty: true }
          )
        } else {
          // If second selected date is after first date, keep order
          setValue(
            name,
            {
              fromDate: selectedDate.fromDate,
              toDate: dateClickedFormatted,
            },
            { shouldDirty: true }
          )
        }
      }
    }
  }

  const closeIfOutside = useCallback(
    (target: HTMLElement) => {
      if (ref.current && target && !ref.current.contains(target)) {
        close()
      }
    },
    [close, ref]
  )

  function closeOnBlur(evt: FocusEvent) {
    if (isOpen) {
      const target = evt.relatedTarget as HTMLElement
      closeIfOutside(target)
    }
  }

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

  const selectedFromDate = dt(selectedDate.fromDate)
    .locale(lang)
    .format("ddd D MMM")
  const selectedToDate = !!selectedDate.toDate
    ? dt(selectedDate.toDate).locale(lang).format("ddd D MMM")
    : ""

  return (
    <div
      className={styles.container}
      onBlur={(e) => {
        closeOnBlur(e.nativeEvent)
      }}
      data-isopen={isOpen}
      ref={ref}
    >
      <button
        className={styles.btn}
        onFocus={showOnFocus}
        onClick={() => setIsOpen(true)}
        type="button"
      >
        <Body className={styles.body} asChild color="uiTextHighContrast">
          <span>
            {intl.formatMessage(
              {
                defaultMessage: "{selectedFromDate} - {selectedToDate}",
              },
              {
                selectedFromDate,
                selectedToDate,
              }
            )}
          </span>
        </Body>
      </button>
      <input {...register("date.fromDate")} type="hidden" />
      <input {...register("date.toDate")} type="hidden" />
      <div aria-modal className={styles.hideWrapper} role="dialog">
        <DatePickerRangeDesktop
          close={close}
          handleOnSelect={handleSelectDate}
          // DayPicker lib needs Daterange in form as below to show appropriate UI
          selectedRange={{
            from: dt(selectedDate.fromDate).toDate(),
            to: selectedDate.toDate
              ? dt(selectedDate.toDate).toDate()
              : undefined,
          }}
        />
        <DatePickerRangeMobile
          close={close}
          handleOnSelect={handleSelectDate}
          // DayPicker lib needs Daterange in form as below to show appropriate UI
          selectedRange={{
            from: dt(selectedDate.fromDate).toDate(),
            to: selectedDate.toDate
              ? dt(selectedDate.toDate).toDate()
              : undefined,
          }}
        />
      </div>
    </div>
  )
}
