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

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

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

import ErrorMessage from "../ErrorMessage"
import { DateName } from "./date"

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

import type { Key } from "react-aria-components"

import type { DateProps } from "./date"

export default function DateSelect({ name, registerOptions = {} }: DateProps) {
  const intl = useIntl()
  const { control, setValue, formState, watch } = useFormContext()
  const { field, fieldState } = useController({
    control,
    name,
    rules: registerOptions,
  })

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

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

  const currentYear = new Date().getFullYear()
  const years = rangeArray(1900, currentYear - 18)
    .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, daysInMonth).map((day) => ({
    value: day,
    label: `${day}`,
  }))

  const dayLabel = intl.formatMessage({ id: "Day" })
  const monthLabel = intl.formatMessage({ id: "Month" })
  const yearLabel = intl.formatMessage({ id: "Year" })

  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)
      }
    }

    if (year && month && day) {
      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)
  }

  return (
    <DatePicker
      aria-label={intl.formatMessage({ id: "Select date of birth" })}
      isRequired={!!registerOptions.required}
      isInvalid={!formState.isValid}
      name={name}
      ref={field.ref}
      value={dateValue}
      data-testid={name}
    >
      <Group>
        <DateInput className={styles.container}>
          {(segment) => {
            switch (segment.type) {
              case "day":
                return (
                  <div
                    className={`${styles.day} ${fieldState.invalid ? styles.invalid : ""}`}
                  >
                    <Select
                      aria-label={dayLabel}
                      items={days}
                      label={dayLabel}
                      name={DateName.day}
                      onSelect={(key: Key) =>
                        setValue(DateName.day, Number(key))
                      }
                      placeholder={dayLabel}
                      required
                      tabIndex={3}
                      value={segment.isPlaceholder ? undefined : segment.value}
                    />
                  </div>
                )
              case "month":
                return (
                  <div
                    className={`${styles.month} ${fieldState.invalid ? styles.invalid : ""}`}
                  >
                    <Select
                      aria-label={monthLabel}
                      items={months}
                      label={monthLabel}
                      name={DateName.month}
                      onSelect={(key: Key) =>
                        setValue(DateName.month, Number(key))
                      }
                      placeholder={monthLabel}
                      required
                      tabIndex={2}
                      value={segment.isPlaceholder ? undefined : segment.value}
                    />
                  </div>
                )
              case "year":
                return (
                  <div
                    className={`${styles.year} ${fieldState.invalid ? styles.invalid : ""}`}
                  >
                    <Select
                      aria-label={yearLabel}
                      items={years}
                      label={yearLabel}
                      name={DateName.year}
                      onSelect={(key: Key) =>
                        setValue(DateName.year, Number(key))
                      }
                      placeholder={yearLabel}
                      required
                      tabIndex={1}
                      value={segment.isPlaceholder ? undefined : segment.value}
                    />
                  </div>
                )
              default:
                /** DateInput forces return of ReactElement */
                return <></>
            }
          }}
        </DateInput>
      </Group>
      <ErrorMessage errors={formState.errors} name={field.name} />
    </DatePicker>
  )
}

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()
}
