import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { SDatePicker } from './s-date-picker';
import { DatePickerProps, PopupPosition } from './types';
import { DateInput, DatePickerPopup } from './components';
import { isRangeProps } from './guards';
import { getNearestScrollableParentElement } from 'utils/dom';
import { getPopupPosition } from 'common/components/form/date-picker/transducers';

export const DatePicker = React.memo((props: DatePickerProps) => {
  const [isPopupVisible, setIsPopupVisible] = useState(false);
  const [isInputFocused, setIsInputFocused] = useState(false);
  const [isValueInvalid, setIsValueInvalid] = useState(false);
  const [position, setPosition] = useState<PopupPosition>({ top: 0, left: 0 });
  const triggerRef = useRef<HTMLDivElement>(null);
  const popupRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (triggerRef.current && popupRef.current && isPopupVisible) {
      setPosition(getPopupPosition(props.mode, triggerRef.current, popupRef.current, props.alignment));
    }
  }, [props.alignment, triggerRef, isPopupVisible, props.mode, popupRef]);

  const hidePopup = useCallback(() => isPopupVisible && setIsPopupVisible(false), [isPopupVisible]);
  const showPopup = useCallback(() => !isPopupVisible && !props.disabled && setIsPopupVisible(true), [
    isPopupVisible,
    props.disabled,
  ]);

  const sidebar = useMemo(() => (isRangeProps(props) ? props.renderSidebar?.(hidePopup) : undefined), [
    props,
    hidePopup,
  ]);

  const onInputFocus = useCallback(() => {
    setIsInputFocused(true);
    showPopup();
  }, [showPopup]);

  const onInputBlur = useCallback(() => {
    setIsInputFocused(false);
  }, []);

  const onInputClick = useCallback(() => {
    if (isInputFocused) {
      showPopup();
    }
  }, [isInputFocused, showPopup]);

  const onClickOutside = useCallback(() => {
    if (isInputFocused) return;
    hidePopup();
  }, [isInputFocused, hidePopup]);

  useEffect(() => {
    const nearestSrollableContainer = getNearestScrollableParentElement(triggerRef.current);

    nearestSrollableContainer?.addEventListener('scroll', hidePopup);

    return () => {
      nearestSrollableContainer?.removeEventListener('scroll', hidePopup);
    };
  }, [triggerRef, hidePopup]);

  useEffect(() => {
    window.addEventListener('resize', hidePopup);

    return () => window.removeEventListener('resize', hidePopup);
  }, [hidePopup]);

  const onValueChange = useCallback(
    (value: [Date, Date] & Date) => {
      hidePopup();
      if (!props.onChange || !value) return;
      props.onChange(value);
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [props.onChange, hidePopup, props.value]
  );
  return (
    <SDatePicker className={props.className}>
      {isRangeProps(props) ? (
        <>
          <DateInput
            ref={triggerRef}
            onFocus={onInputFocus}
            onBlur={onInputBlur}
            onClick={onInputClick}
            value={props.value}
            onChange={onValueChange}
            mode={props.mode}
            onErrorChange={setIsValueInvalid}
            shouldResetInputToDateValue={!isPopupVisible}
            bgStyle={props.bgStyle}
            maxPeriodDays={props.maxPeriodDays}
            availableDates={props.availableDates}
            disabled={props.disabled}
            dataSelector={props.dataSelector}
            className={props.inputContainerClassName}
            inputClassName={props.inputClassName}
            withInputIcon={props.withInputIcon}
          />
          <DatePickerPopup
            ref={popupRef}
            position={position}
            isVisible={isPopupVisible}
            onChange={onValueChange}
            value={props.value}
            mode={props.mode}
            alignment={props.alignment}
            onClickOutside={onClickOutside}
            isValueInvalid={isValueInvalid}
            maxPeriodDays={props.maxPeriodDays}
            annotationMessage={props.annotationMessage}
            sidebar={sidebar}
            availableDates={props.availableDates}
            disabled={props.disabled}
          />
        </>
      ) : (
        <>
          <DateInput
            ref={triggerRef}
            onFocus={onInputFocus}
            onBlur={onInputBlur}
            onClick={onInputClick}
            value={props.value}
            onChange={onValueChange}
            mode={props.mode}
            onErrorChange={setIsValueInvalid}
            shouldResetInputToDateValue={!isPopupVisible}
            bgStyle={props.bgStyle}
            disabled={props.disabled}
            availableDates={props.availableDates}
            dataSelector={props.dataSelector}
            className={props.inputContainerClassName}
            inputClassName={props.inputClassName}
            withInputIcon={props.withInputIcon}
          />
          <DatePickerPopup
            ref={popupRef}
            position={position}
            isVisible={isPopupVisible}
            onChange={onValueChange}
            onClickOutside={onClickOutside}
            value={props.value}
            mode={props.mode}
            annotationMessage={props.annotationMessage}
            alignment={props.alignment}
            isValueInvalid={isValueInvalid}
            availableDates={props.availableDates}
            disabled={props.disabled}
          />
        </>
      )}
    </SDatePicker>
  );
});
