"use client";

import classNames from "classnames/dedupe";
import type { FocusEvent, RefAttributes } from "react";
import { useEffect, useRef, useState } from "react";
import { mergeRefs } from "../../utils/mergeRefs";
import {
  TextField,
  type TextFieldElementType,
  type TextFieldProps,
} from "../TextField";
import { convertToDateFormat, isValidDate } from "./validation";

export interface DateFieldProps
  extends Omit<TextFieldProps, "type">, RefAttributes<TextFieldElementType> {
  /** The earliest date allowed for the input. */
  min?: string;
  /** The latest date allowed for the input. */
  max?: string;
  /** Date format to use. */
  format?: string;
}

export const MIN_MAX_ERROR =
  '"min" value should not be greater than "max" value.';

export const DateField = ({ ref, ...props }: DateFieldProps) => {
  const {
    min,
    max,
    format,
    className,
    errorMessage,
    defaultValue,
    value,
    ...otherProps
  } = props;
  const [error, setError] = useState<string | undefined>(errorMessage);
  const [isInvalid, setIsInvalid] = useState<boolean | undefined>(undefined);
  const localRef = useRef<TextFieldElementType>(null);
  const classes = classNames("mobius-date-field", className);

  // If a custom format is provided, convert the min, max,
  // and defaultValue dates to that format
  const formattedMin = min ? convertToDateFormat(min, format) : undefined;
  const formattedMax = max ? convertToDateFormat(max, format) : undefined;
  const formattedDefaultValue = defaultValue
    ? convertToDateFormat(defaultValue, format)
    : undefined;
  const formattedValue = value ? convertToDateFormat(value, format) : undefined;

  const setInvalidState = (error?: string) => {
    setError(error);
    setIsInvalid(true);
  };

  const setValidState = () => {
    setError(props.errorMessage);
    setIsInvalid(false);
  };

  // Validate min and max values
  useEffect(() => {
    if (!isValidDate(min, format)) {
      setInvalidState(`Invalid min date: ${min}`);
      return;
    }
    if (!isValidDate(max, format)) {
      setInvalidState(`Invalid max date: ${max}`);
      return;
    }

    if (min && max) {
      const minDate = new Date(min);
      const maxDate = new Date(max);
      if (minDate > maxDate) {
        setInvalidState(MIN_MAX_ERROR);
      } else {
        setValidState();
      }
    } else {
      setValidState();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [min, max, format]);

  const validate = () => {
    // If 'min' or 'max' values are provided, checkValidity() will
    // validate the date and return a boolean
    const isValidInput = localRef.current?.checkValidity();

    if (!isValidInput) {
      setInvalidState("Invalid date input");
    } else {
      setValidState();
    }
  };

  // User has interacted with the component and navigated away
  const handleBlur = (event: FocusEvent<Element>) => {
    validate();
    otherProps.onBlur?.(event);
  };

  return (
    <TextField
      ref={mergeRefs([localRef, ref])}
      className={classes}
      type="date"
      min={formattedMin}
      max={formattedMax}
      errorMessage={errorMessage ?? error}
      isInvalid={isInvalid}
      defaultValue={formattedDefaultValue}
      value={formattedValue}
      onBlur={handleBlur}
      {...otherProps}
    />
  );
};

DateField.displayName = "DateField";
