import { useEffect, useState } from 'react';
import DatePicker, {
  DatePickerProps,
} from 'react-date-picker/dist/entry.nostyle';
import classnames from 'classnames';
import parseISO from 'date-fns/parseISO';
import { InjectedFieldProps } from '../Field/InjectedFieldProps';
import { FormDefaults } from '../FormDefaults';
import Group, { GroupProps } from '../Group';

export interface DatePickerGroupProps<T>
  extends InjectedFieldProps<T | undefined | null>,
    Omit<
      DatePickerProps,
      keyof InjectedFieldProps<T> | 'name' | 'value' | 'className'
    >,
    Omit<GroupProps, keyof InjectedFieldProps<T> | 'children'> {
  convert: (arg: Date) => T;
}

/**
 * Field for inputting dates. Uses `<Group/>` and `<DatePicker/>`.
 *
 * Uses [react-date-picker](https://www.npmjs.com/package/react-date-picker)
 */
export default function DatePickerGroup<T>({
  input,
  meta,
  label,
  helpText,
  className,
  required,
  disabled,
  convert,
  maxDate = new Date(9999, 11, 31),
  minDate = new Date(1000, 0, 1),
  ...rest
}: DatePickerGroupProps<T>) {
  const [displayDate, setDisplayDate] = useState<Date | null>(null);

  useEffect(() => {
    if (!input.value) {
      setDisplayDate(null);
    } else if (typeof input.value === 'string') {
      if (input.value.indexOf('T') === -1) {
        setDisplayDate(new Date(`${input.value}T00:00:00.000`));
      } else {
        setDisplayDate(new Date(input.value));
      }
    }
  }, [setDisplayDate, input.value]);

  return (
    <Group
      input={input}
      meta={meta}
      label={label}
      helpText={helpText}
      className={classnames(
        className,
        FormDefaults.cssClassPrefix + 'date-picker'
      )}
      required={required}
      disabled={disabled}>
      <div
        onBlur={() => {
          // Manually trigger Formik's onBlur when the user clicks away from the date picker
          input.onBlur();
        }}>
        <DatePicker
          {...rest}
          className={classnames(
            FormDefaults.cssClassPrefix + 'date-picker',
            className
          )}
          value={displayDate}
          onChange={handleChange}
          disabled={disabled}
          required={required}
          maxDate={maxDate}
          minDate={minDate}
        />
      </div>
    </Group>
  );

  function handleChange(e: Date | Date[] | string | undefined) {
    const { onChange } = input;
    if (onChange === null) {
      return;
    }

    if (!e) {
      onChange(undefined);
      setDisplayDate(null);
      return;
    } else if (typeof e === 'string') {
      const parsedValue = parseISO(e.toString().split('T')[0]);
      setDisplayDate(parsedValue);
      onChange(convert(parsedValue));
    } else if (e instanceof Date) {
      const parsedValue = parseISO(
        convertToTimeZoneInsensitiveISOString(e).split('T')[0]
      );
      setDisplayDate(parsedValue);
      onChange(convert(parsedValue));
    } else {
      // e is instanceof Date[]
      const parsedValue = parseISO(
        convertToTimeZoneInsensitiveISOString(e[0]).split('T')[0]
      );
      setDisplayDate(parsedValue);
      onChange(convert(parsedValue));
    }
  }
}

export function convertToTimeZoneInsensitiveISOString(date: Date): string {
  const year = new Intl.DateTimeFormat('en', { year: 'numeric' })
    .format(date)
    .substring(0, 4)
    .padStart(4, '0');
  const month = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(
    date
  );
  const day = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date);
  return `${year}-${month}-${day}T00:00:00.000Z`;
}
