import { isValidDate } from "@web3r/flowerkit/date";
import type { ReactNode, FocusEvent, Ref } from "react";
import {
  useEffect, useCallback, useRef, useState,
} from "react";
import type { ReactMaskOpts } from "react-imask";
import { useIMask } from "react-imask";
import { useCN } from "../../../utils/react/useCN";
import { today } from "../config/constants";
import type { TDatePickerInputProps } from "../config/types";

/**
 * Поле пользовательского ввода. Всегда имеет маску.
 * @returns {ReactNode}
 */
export const DatePickerCustomInput = ({
  baseClass = "formInput",
  extraClasses = {},
  isDisabled = false,
  children,
  onBlur,
  onClick,
  onKeyDown,
  date,
  maxDate,
  minDate,
  pickerRef,
  mask,
  format,
  handleChange,
  isTime,
  isRange,
  isAutoFix,
  timeIntervals,
  placeholder,
  ref,
  ...rest
}: TDatePickerInputProps): ReactNode => {

  const getMaskValue = () => {
    if (typeof mask === "function") {
      const props = mask({
        isRange,
        isAutoFix,
        format,
        minDate,
        maxDate,
        isTime,
        today,
      });
      return !!props ? props : null;
    } else {
      return null;
    }
  };

  // Опции маски (может не быть, если range или маска убрана вручную)
  const [ maskOptions, setMaskOptions ] = useState<ReactMaskOpts | null>(() => getMaskValue());
  const prevMaskOptions = useRef<ReactMaskOpts | null>(maskOptions);

  const {
    setTypedValue,
    setValue,
    ref: maskRef,
    maskRef: maskInstance,
    value: maskValue,
  } = useIMask(maskOptions || {}, {
    onComplete: () => {
      if (pickerRef.current) {
        pickerRef.current.setOpen(false);
      }
    },
    onAccept: (value, instance) => {
      if (instance.masked && typeof instance.masked.parse === "function") {
        const parsedDate = value ? instance.masked.parse(value, instance.masked) : undefined;
        if (!!parsedDate && isValidDate(parsedDate) && !!pickerRef?.current?.calendar) {
          const mm = parsedDate.getMonth();
          pickerRef.current.calendar.changeMonth(mm);
        }
      }
    },
  });

  const { getCN } = useCN(baseClass);

  // Предыдущее значение
  const prevDate = useRef(date);

  // Сброс некорректно введенной даты
  const handleBlur = useCallback((e: FocusEvent<HTMLInputElement>) => {
    if (!!maskOptions) {

      const masked = maskInstance.current?.masked;

      // Проверка готовности маски
      if (!!masked && typeof masked.parse === "function") {

        // То, что ввёл пользователь
        const currentValue = e.target.value;

        // Парсинг данных из маски
        const parsedDate = currentValue ? masked.parse(currentValue, masked) : undefined;

        if (!!parsedDate && isValidDate(parsedDate)) { // дата пользователя валидна
          const formattedValue = (date: Date) => {
            return typeof masked.format === "function" ? masked.format(date as never, masked) : "";
          };
          switch (true) {
            // пользователь ввел дату больше, чем нужно
            case (parsedDate > maxDate!):
              setValue(isAutoFix ? formattedValue(maxDate!) : "");
              handleChange(isAutoFix ? maxDate : null, e);
              console.debug(`[DatePicker] Parsed date "${parsedDate}" fixed to max "${maxDate}"`);
              break;
            // пользователь ввел дату меньше, чем нужно
            case (parsedDate < minDate!):
              setValue(isAutoFix ? formattedValue(minDate!) : "");
              handleChange(isAutoFix ? minDate : null, e);
              console.debug(`[DatePicker] Parsed date "${parsedDate}" fixed to min "${minDate}"`);
              break;
            default:
              // пользователь ввёл корректную дату
              if (isTime && Number.isFinite(timeIntervals)) { // дополнительная проверка на кратность минут, если `isTime`
                const MM = parsedDate.getMinutes();
                if (MM % timeIntervals!) {
                  const roundedMM = Math.round(MM / timeIntervals!) * timeIntervals!;
                  parsedDate.setMinutes(roundedMM);
                  setValue(formattedValue(parsedDate));
                  console.debug(`[DatePicker] Parsed date "${parsedDate}" fixed: corrected minutes "${roundedMM}"`);
                }
              }
              handleChange(parsedDate, e);
          }
        } else { // дата пользователя невалидна
          setValue("");
          setTypedValue("");
          handleChange(null, e);
        }
      }
    }
    if (typeof onBlur === "function") {
      onBlur(e);
    }
  }, [ maskOptions, onBlur, maskInstance, maxDate, setValue, isAutoFix, handleChange, minDate, isTime, timeIntervals, setTypedValue ]);

  useEffect(() => {
    const updatedMask = getMaskValue();
    if (updatedMask !== prevMaskOptions.current) {
      setMaskOptions(updatedMask);
      prevMaskOptions.current = updatedMask;
    }
  }, [ isRange, isAutoFix, format, minDate, maxDate, isTime ]);

  useEffect(() => {
    if (!!maskOptions) {
      if (isValidDate(date)) { // валидная дата
        const masked = maskInstance?.current?.masked;
        if (!!masked && typeof masked?.format === "function") {
          const formattedValue = masked.format(date as never, masked);
          setValue(formattedValue);
        }
      } else if (prevDate.current !== date) { // сброс даты
        setValue("");
        maskInstance.current!.value = ""; // обновление UI iMask
      }
      prevDate.current = date;
    }
  }, [ date ]);

  const computedProps = {
    onChange: () => {}, // prevent react warning
    onBlur: handleBlur,
    className: getCN("", extraClasses),
    disabled: isDisabled,
    placeholder,
    onClick,
    onKeyDown,
    value: !!maskOptions ? maskValue : rest.value, // conditional value
    ref: (!!maskOptions ? maskRef : ref) as Ref<HTMLInputElement>, // conditional ref,
    max: undefined,
    min: undefined,
    inputMode: "numeric" as const,
  };

  return (
    <>
      <input {...computedProps} />
      {children}
    </>
  );

};

