"use client"
import { parseDate } from "@internationalized/date"
import { useEffect } from "react"
import { useController, useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl"

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

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

import useLang from "@/hooks/useLang"
import { getLocalizedMonthName } from "@/utils/dateFormatting"
import { rangeArray } from "@/utils/rangeArray"

import ErrorMessage from "../ErrorMessage"
import { DateName, type DateProps } from "./date"

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

export default function DateSelect({ name, registerOptions = {} }: DateProps) {
  const intl = useIntl()
  const lang = useLang()

  const { control, setValue, formState, watch } = useFormContext()
  const { field, fieldState } = useController({
    control,
    name,
    rules: registerOptions,
  })

  const currentDateValue: string = useWatch({ name })
  const year = watch(DateName.year)
  const month = watch(DateName.month)
  const day = watch(DateName.day)

  const minAgeDate = dt().subtract(18, "year").toDate() // age 18
  const minAgeYear = minAgeDate.getFullYear()
  const minAgeMonth = year === minAgeYear ? minAgeDate.getMonth() + 1 : null
  const minAgeDay =
    Number(year) === minAgeYear && Number(month) === minAgeMonth
      ? minAgeDate.getDate()
      : null

  const months = rangeArray(1, minAgeMonth ?? 12).map((month) => ({
    value: month,
    label: getLocalizedMonthName(month, lang),
  }))

  const years = rangeArray(1900, minAgeYear)
    .reverse()
    .map((year) => ({ value: year, label: year.toString() }))

  // Calculate available days based on selected year and month
  const daysInMonth = getDaysInMonth(
    year ? Number(year) : null,
    month ? Number(month) - 1 : null
  )

  const days = rangeArray(1, minAgeDay ?? daysInMonth).map((day) => ({
    value: day,
    label: `${day}`,
  }))

  useEffect(() => {
    if (formState.isSubmitting) return

    if (month && day) {
      const maxDays = getDaysInMonth(
        year ? Number(year) : null,
        Number(month) - 1
      )
      const adjustedDay = Number(day) > maxDays ? maxDays : Number(day)

      if (adjustedDay !== Number(day)) {
        setValue(DateName.day, adjustedDay)
      }
    }

    const newDate = dt()
      .year(Number(year))
      .month(Number(month) - 1)
      .date(Number(day))

    if (newDate.isValid()) {
      setValue(name, newDate.format("YYYY-MM-DD"), {
        shouldDirty: true,
        shouldTouch: true,
        shouldValidate: true,
      })
    }
  }, [year, month, day, setValue, name, formState.isSubmitting])

  let dateValue = null
  try {
    /**
     * parseDate throws when its not a valid
     * date, but we can't check isNan since
     * we recieve the date as "1999-01-01"
     */
    dateValue = dt(currentDateValue).isValid()
      ? parseDate(currentDateValue)
      : null
  } catch (error) {
    console.warn("Known error for parse date in DateSelect: ", error)
  }

  useEffect(() => {
    if (formState.isSubmitting) return

    if (!(day && month && year) && dateValue) {
      setValue(DateName.day, Number(dateValue.day))
      setValue(DateName.month, Number(dateValue.month))
      setValue(DateName.year, Number(dateValue.year))
    }
  }, [setValue, formState.isSubmitting, dateValue, day, month, year])

  return (
    <>
      <div className={styles.container}>
        <div className={styles.segment}>
          <Select
            items={days}
            label={intl.formatMessage({
              defaultMessage: "Day",
            })}
            name={DateName.day}
            onSelectionChange={(key) => setValue(DateName.day, Number(key))}
            isRequired
            enableFiltering
            isInvalid={fieldState.invalid}
            onBlur={field.onBlur}
            defaultSelectedKey={dateValue?.day}
            data-testid={DateName.day}
          />
        </div>
        <div className={styles.segment}>
          <Select
            items={months}
            label={intl.formatMessage({
              defaultMessage: "Month",
            })}
            name={DateName.month}
            onSelectionChange={(key) => setValue(DateName.month, Number(key))}
            isRequired
            enableFiltering
            isInvalid={fieldState.invalid}
            onBlur={field.onBlur}
            defaultSelectedKey={dateValue?.month}
            data-testid={DateName.month}
          />
        </div>
        <div className={styles.segment}>
          <Select
            items={years}
            label={intl.formatMessage({
              defaultMessage: "Year",
            })}
            name={DateName.year}
            onSelectionChange={(key) => setValue(DateName.year, Number(key))}
            isRequired
            enableFiltering
            isInvalid={fieldState.invalid}
            onBlur={field.onBlur}
            defaultSelectedKey={dateValue?.year}
            data-testid={DateName.year}
          />
        </div>
      </div>
      <ErrorMessage errors={formState.errors} name={field.name} />
    </>
  )
}

function getDaysInMonth(year: number | null, month: number | null): number {
  if (month === null) {
    return 31
  }

  // If month is February and no year selected, return minimum.
  if (month === 1 && !year) {
    return 28
  }

  const yearToUse = year ?? new Date().getFullYear()
  return dt(`${yearToUse}-${month + 1}-01`).daysInMonth()
}
