import { useEffect, useRef, useState } from 'react'

import Box from '../box'
import Button, { LineButton } from '../button'
import Calendar, { DateRange, maxDateTimestamp, minDateTimestamp } from '../calendar'
import { omitThemingProps, useStyleConfig, vui } from '../core'
import Popover from '../popover'
import { cs, useClickOutside } from '../utils'
import { DateInput } from './dateInput'
import { DatePickerProps } from './datePicker.types'

/** Select or input a date. */
export const DatePicker = vui<'span', DatePickerProps>((props, ref) => {
  const {
    autoOpened,
    boundaries,
    popupPosition,
    placement,
    selectedDate: selectedDateProps,
    showClearButton,
    slotBottom,
    slotTop,
    onSelected,
    children,
    className,
    ...rest
  } = omitThemingProps(props)
  const styles = useStyleConfig('DatePicker', props)

  const [selectedDate, setSelectedDate] = useState<any>(selectedDateProps)
  const [isOpen, setIsOpen] = useState(!selectedDate && autoOpened)
  const [isInvalidInputDate, setIsInvalidInputDate] = useState(true)
  const [isInvalidSelectedDate, setIsInvalidSelectedDate] = useState(!selectedDate)
  const initialDateRef = useRef<Date | undefined>(selectedDateProps)
  const isClearButtonVisible = showClearButton && initialDateRef.current

  const isStartDateInRange = (date: Date) =>
    !!boundaries?.startDate && !!date ? date.getTime() - boundaries.startDate.getTime() >= 0 : true

  const isEndDateInRange = (date: Date) =>
    !!boundaries?.endDate && !!date ? date.getTime() - boundaries.endDate.getTime() <= 0 : true

  const isValidDateRange = (date: Date) => {
    const minDateTime = new Date(minDateTimestamp)
    const maxDateTime = new Date(maxDateTimestamp)
    return date >= minDateTime || date <= maxDateTime
  }

  useEffect(() => {
    if (!isOpen) {
      setSelectedDate(selectedDateProps)
      initialDateRef.current = selectedDateProps
    }
  }, [isOpen, selectedDateProps])

  useEffect(() => {
    setIsInvalidSelectedDate(
      (selectedDate && !isStartDateInRange(selectedDate)) ||
        (selectedDate && !isEndDateInRange(selectedDate)) ||
        (selectedDate && !isValidDateRange(selectedDate)) ||
        !selectedDate,
    )
  }, [selectedDate])

  const onApplyDates = (date?: Date) => {
    onSelected?.(date)
    initialDateRef.current = date
    setIsOpen(false)
  }

  const onClear = () => {
    onClose()
    onSelected?.(undefined)
  }

  const onClose = () => {
    setSelectedDate(undefined)
    setIsOpen(false)
  }

  const onToggle = () => {
    if (isOpen) onClose()
    setIsOpen(!isOpen)
  }

  const selectDate = (range: DateRange) => setSelectedDate(range?.startDate)

  const onDateInputValidate = (date?: Date) => {
    const minDateTime = new Date(minDateTimestamp)
    const maxDateTime = new Date(maxDateTimestamp)

    if (date) {
      const conditions: [boolean, string][] = [
        [isNaN(date.getTime()), 'Supported format is YYYY-MM-DD'],
        [date < minDateTime, 'Invalid date, the minimum date time is 1753-01-02'],
        [date > maxDateTime, 'Invalid date, the maximum date time is 9000-12-31'],
        [!!boundaries?.startDate && !isStartDateInRange(date), 'Invalid date'],
        [!!boundaries?.endDate && !isEndDateInRange(date), 'Invalid date'],
      ]
      const [validateErrorPair] = conditions.filter(([result]) => result)

      if (validateErrorPair) {
        const [_, message] = validateErrorPair
        setIsInvalidInputDate(true)
        return message
      }
    }
    return ''
  }

  return (
    <Box className={cs('vui-datePicker', className)} position="relative" ref={ref} {...styles} {...rest}>
      <Popover
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        placement={placement ? placement : popupPosition === 'right' ? 'bottom-end' : undefined}
      >
        <Popover.Trigger as={Box} onClick={onToggle}>
          {children}
        </Popover.Trigger>
        <Popover.Content column>
          {!!slotTop && <Box px={2}>{slotTop}</Box>}
          <Box display="block" mb={0} mt={2} mx={2}>
            <DateInput
              autoFocus
              initialDate={selectedDate}
              onChange={newDate => setSelectedDate(newDate)}
              onValidate={onDateInputValidate}
              setValidateStatus={inValid => setIsInvalidInputDate(!inValid)}
            />
          </Box>
          <Box mx={2} my={1}>
            <Calendar
              boundaries={boundaries ?? { startDate: undefined, endDate: undefined }}
              fixedNumberOfWeeks
              fontWeight={500}
              minH={310}
              mode="exact"
              onSelectDates={range => selectDate(range)}
              selectedDates={{ startDate: selectedDate, endDate: undefined }}
              w="100%"
            />
          </Box>
          <Box overflow="unset" pt={0.5}>
            <Box alignItems="center" flex={1} ml="auto" pb={2} px={2}>
              {isClearButtonVisible && (
                <LineButton flex={1} onClick={onClear} size="md">
                  Clear
                </LineButton>
              )}
              <Button
                disabled={isInvalidSelectedDate || isInvalidInputDate}
                flex={1}
                ml={isClearButtonVisible ? 1 : 0}
                onClick={() => onApplyDates(selectedDate)}
                size="md"
              >
                Apply
              </Button>
            </Box>
          </Box>
          {!!slotBottom && <Box px={2}>{slotBottom}</Box>}
        </Popover.Content>
      </Popover>
    </Box>
  )
})

DatePicker.displayName = 'DatePicker'

export default DatePicker
