/**
 * TEAM: frontend_infra
 * @flow
 */

import * as React from "react";
import ReactDatePicker, {type ReactDatePickerProps} from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import "./DatePickerStylesOverride.scss";

/** DO NOT USE this component directly, prefer CalendarDateInput or DateTimeInput */

/**
 * Wrapper for react-date-picker that UTC conversions. This is done manually in
 * this component since `utcOffset` prop was no longer supported in later
 * versions of react-datepicker
 *
 * This UTC conversion implementation is based off of:
 * https://github.com/Hacker0x01/react-datepicker/issues/1787#issuecomment-770313939
 */

function convertUTCToLocalDate(utcDate: Date) {
  if (!utcDate) {
    return utcDate;
  }

  let localDate = new Date(utcDate);

  localDate = new Date(
    localDate.getUTCFullYear(),
    localDate.getUTCMonth(),
    localDate.getUTCDate()
  );

  return localDate;
}

function convertLocalToUTCDate(localDate: Date) {
  if (!localDate) {
    return localDate;
  }

  let utcDate = new Date(localDate);

  utcDate = new Date(
    Date.UTC(utcDate.getFullYear(), utcDate.getMonth(), utcDate.getDate())
  );

  return utcDate;
}

// TODO: Do UTC conversions for these props +renderDayContents?: (dayOfMonth:
// number, date?: Date) => React$Node;

// NOTE: Not all conversions tested and not conversions that need to be done are
// covered

const UTC_TO_LOCAL_CONVERSIONS = [
  "selected",
  "startDate",
  "openToDate",
  "endDate",
  "maxDate",
  "maxTime",
  "minDate",
  "minTime",
];

const UTC_TO_LOCAL_CONVERSIONS_ARRAY = [
  "excludeDates",
  "excludeTimes",
  "includeDates",
  "includeTimes",
  "injectTimes",
];

function localToUTCFunctionConversion<R, O>(
  func: (Date, ...args: Array<O>) => R
): Date => R {
  return (date, ...args) => func(convertLocalToUTCDate(date), ...args);
}

type Props = {|
  ...ReactDatePickerProps,
  // We add onMount to support uses of Popper with React.Suspense, where Popper
  // should recalculate placement after this component mounts after loading
  // lazily
  +onMount?: () => void,
  // Disable UTC conversions, relevant for when using times
  +noUTC?: boolean,
|};

export default (React.forwardRef<Props, ReactDatePicker>(function DatePicker(
  props: Props,
  ref:
    | ((null | ReactDatePicker) => mixed)
    | {current: null | ReactDatePicker, ...}
): React.Node {
  const {onMount, noUTC, ...baseProps} = {
    ...props,
  };
  const augmentedProps = {
    ...baseProps,
    onChange: (date, event) => {
      let processedDate: Date | [Date, Date] | null = null;

      if (Array.isArray(date)) {
        processedDate = [
          convertLocalToUTCDate(date[0]),
          convertLocalToUTCDate(date[1]),
        ];
      } else if (date != null) {
        processedDate = convertLocalToUTCDate(date);
      }

      props.onChange(processedDate, event);
    },
    dayClassName:
      props.dayClassName && localToUTCFunctionConversion(props.dayClassName),
    weekDayClassName:
      props.weekDayClassName &&
      localToUTCFunctionConversion(props.weekDayClassName),
    monthClassName:
      props.monthClassName &&
      localToUTCFunctionConversion(props.monthClassName),
    timeClassName:
      props.timeClassName && localToUTCFunctionConversion(props.timeClassName),
    filterDate:
      props.filterDate && localToUTCFunctionConversion(props.filterDate),
    filterTime:
      props.filterTime && localToUTCFunctionConversion(props.filterTime),
    formatWeekNumber:
      props.formatWeekNumber &&
      localToUTCFunctionConversion(props.formatWeekNumber),
    onDayMouseEnter:
      props.onDayMouseEnter &&
      localToUTCFunctionConversion(props.onDayMouseEnter),
    onMonthChange:
      props.onMonthChange && localToUTCFunctionConversion(props.onMonthChange),
    onYearChange:
      props.onYearChange && localToUTCFunctionConversion(props.onYearChange),
    onSelect: props.onSelect && localToUTCFunctionConversion(props.onSelect),
    onWeekSelect:
      props.onWeekSelect && localToUTCFunctionConversion(props.onWeekSelect),
  };

  UTC_TO_LOCAL_CONVERSIONS.forEach(propToModify => {
    const existingValue = augmentedProps[propToModify];

    if (existingValue)
      augmentedProps[propToModify] = convertUTCToLocalDate(existingValue);
  });

  UTC_TO_LOCAL_CONVERSIONS_ARRAY.forEach(propToModify => {
    const existingValue = augmentedProps[propToModify];

    if (existingValue)
      augmentedProps[propToModify] = existingValue.map(dateItem =>
        convertUTCToLocalDate(dateItem)
      );
  });

  React.useLayoutEffect(() => {
    if (onMount) onMount();
  }, [onMount]);

  return (
    <ReactDatePicker {...(noUTC ? baseProps : augmentedProps)} ref={ref} />
  );
}): React.AbstractComponent<Props, ReactDatePicker>);
