/* eslint-disable react/jsx-props-no-spreading */
import React, { FunctionComponent, useState, useEffect, MouseEvent, useRef } from 'react';
import moment from 'moment';
import omit from 'lodash/omit';
import { StyledDatePicker } from '../styled-component';
import { DateRangePickerProps, CalendarDate, RangePickerDatesType, MonthChangeType, HtmlElementsTwoMonthType } from '../types.d';
import { Button } from '../../Button';
import { Title } from '../../Title';
import { Divider } from '../../Divider';
import { Wrapper } from '../../Wrapper';
import { useTheme } from '../../Theme';

export const DateRangePicker: FunctionComponent<DateRangePickerProps> = (props) => {
  const theme = useTheme();
  const {
    wrapperProps,
    isVisible,
    firstInitDate,
    secondInitDate,
    firstDatePicker,
    secondDatePicker,
    buttonSave,
    buttonCancel,
    onChange,
  } = props;

  const dateStringFormat = 'DD MMMM YYYY';

  const [firstDatePickerDate, setFirstDatePicker] = useState<CalendarDate>(moment(firstInitDate).startOf('month'));
  const [secondDatePickerDate, setSecondDatePicker] = useState<CalendarDate>(moment(secondInitDate).startOf('month'));
  const firstMonthMaxDate = moment(secondInitDate).set('date', 0);
  const secondMonthMinDate = moment(secondInitDate).set('date', 1);

  const [monthChangeDate, setMonthChangeDate] = useState<MonthChangeType>({ from: '', date: null });

  const initRangePickerDates = { firstDate: null, secondDate: null };
  const [rangePickerDates, setRangePickerDates] = useState<RangePickerDatesType>(initRangePickerDates);

  const monthsContainer = useRef<HTMLDivElement>(null);

  useEffect(() => {
    setFirstDatePicker(moment(firstInitDate).startOf('month'));
    setSecondDatePicker(moment(secondInitDate).startOf('month'));
    setRangePickerDates(initRangePickerDates);
  }, [isVisible]);

  useEffect(() => {
    const elements = getHtmlElements();
    if (rangePickerDates.secondDate === null && elements !== null) {
      Array.from(elements.selectedDays).forEach((e) => e.classList.remove('MuiPickersDay-daySelected'));
      Array.from(elements.allDays).forEach((day) => day.setAttribute('style', ''));
      onChange({ firstDate: rangePickerDates.firstDate, secondDate: null });
    }

    if (rangePickerDates.firstDate !== null && rangePickerDates.secondDate !== null) {
      addSelectedStyleForDay();
      onChange(sortDatesByIncrease(rangePickerDates));
    }
  }, [rangePickerDates, monthChangeDate, theme.type]);

  useEffect(() => {
    if (monthChangeDate.from === 'firstMonth') setFirstDatePicker(monthChangeDate.date);
    if (monthChangeDate.from === 'secondMonth') setSecondDatePicker(monthChangeDate.date);
  }, [monthChangeDate]);

  const sortDatesByIncrease = (rangeDates: RangePickerDatesType) => {
    const firstDateValue = moment(rangePickerDates.firstDate).valueOf();
    const secondDateValue = moment(rangePickerDates.secondDate).valueOf();
    const isSecondValueGreater = secondDateValue > firstDateValue;
    if (isSecondValueGreater) return rangeDates;
    return { firstDate: rangeDates.secondDate, secondDate: rangeDates.firstDate };
  };

  const getDateValue = (dateStr: string) => moment(dateStr, dateStringFormat).valueOf();

  const handleMouseOver = (event: MouseEvent, month: string) => {
    if (rangePickerDates.firstDate !== null && rangePickerDates.secondDate !== null) return;
    const elements = getHtmlElements();
    if (elements === null) return;
    const { firstMonthDays, secondMonthDays, firstHeaderWithMonthAndYear, secondHeaderWithMonthAndYear } = elements;

    const firstDateString = moment(rangePickerDates.firstDate).format(dateStringFormat);
    const firstDateValue = getDateValue(firstDateString);

    const headerWithMonthAndYear =
      month === 'firstMonth' ? firstHeaderWithMonthAndYear.innerHTML : secondHeaderWithMonthAndYear.innerHTML;

    const currentDateString = `${(event.target as HTMLElement).innerText} ${headerWithMonthAndYear}`;
    const currentValue = getDateValue(currentDateString);

    Array.from(firstMonthDays).forEach((day) => {
      day.setAttribute('style', '');

      const dateString = `${day.innerHTML} ${firstHeaderWithMonthAndYear.innerHTML}`;
      const dateValue = getDateValue(dateString);

      const daySelectedCondition = dateValue === firstDateValue;
      if (day.parentElement !== null && daySelectedCondition) day.parentElement.classList.add('MuiPickersDay-daySelected');
      if ((dateValue > firstDateValue && dateValue < currentValue) || (dateValue < firstDateValue && dateValue > currentValue))
        day.setAttribute('style', `color: ${theme.pallet.primaryColor}`);
    });

    Array.from(secondMonthDays).forEach((day) => {
      day.setAttribute('style', '');

      const dateString = `${day.innerHTML} ${secondHeaderWithMonthAndYear.innerHTML}`;
      const dateValue = getDateValue(dateString);

      const daySelectedCondition = dateValue === firstDateValue;
      if (day.parentElement !== null && daySelectedCondition) day.parentElement.classList.add('MuiPickersDay-daySelected');
      if ((dateValue > firstDateValue && dateValue < currentValue) || (dateValue < firstDateValue && dateValue > currentValue))
        day.setAttribute('style', `color: ${theme.pallet.primaryColor}`);
    });
  };

  const addSelectedStyleForDay = () => {
    const elements = getHtmlElements();
    if (elements === null) return;
    const { firstMonthDays, secondMonthDays, firstHeaderWithMonthAndYear, secondHeaderWithMonthAndYear } = elements;

    const firstDateString = moment(rangePickerDates.firstDate).format(dateStringFormat);
    const firstDateValue = getDateValue(firstDateString);
    const secondDateString = moment(rangePickerDates.secondDate).format(dateStringFormat);
    const secondDateValue = getDateValue(secondDateString);

    const isSecondValueGreater = secondDateValue > firstDateValue;
    const lowerValue = isSecondValueGreater ? firstDateValue : secondDateValue;
    const greaterValue = isSecondValueGreater ? secondDateValue : firstDateValue;

    Array.from(firstMonthDays).forEach((day) => {
      day.setAttribute('style', '');
      const dateString = `${day.innerHTML} ${firstHeaderWithMonthAndYear.innerHTML}`;
      const dateValue = getDateValue(dateString);
      const daySelectedCondition = dateValue === firstDateValue || dateValue === secondDateValue;
      if (day.parentElement !== null && daySelectedCondition) day.parentElement.classList.add('MuiPickersDay-daySelected');

      if (rangePickerDates.firstDate !== null && rangePickerDates.secondDate !== null) {
        const isDaysInRange = dateValue > lowerValue && dateValue < greaterValue;
        if (isDaysInRange) day.setAttribute('style', `color: ${theme.pallet.primaryColor}`);
      }
    });

    Array.from(secondMonthDays).forEach((day) => {
      day.setAttribute('style', '');
      const dateString = `${day.innerHTML} ${secondHeaderWithMonthAndYear.innerHTML}`;
      const dateValue = getDateValue(dateString);
      const daySelectedCondition = dateValue === firstDateValue || dateValue === secondDateValue;
      if (day.parentElement !== null && daySelectedCondition) day.parentElement.classList.add('MuiPickersDay-daySelected');

      if (rangePickerDates.firstDate !== null && rangePickerDates.secondDate !== null) {
        const isDaysInRange = dateValue > lowerValue && dateValue < greaterValue;
        if (isDaysInRange) day.setAttribute('style', `color: ${theme.pallet.primaryColor}`);
      }
    });
  };

  const getHtmlElements = (): HtmlElementsTwoMonthType | null => {
    if (monthsContainer === null || monthsContainer.current === null) return null;

    const selectedDays = monthsContainer.current.querySelectorAll('.MuiPickersDay-daySelected');
    const allDays = monthsContainer.current.querySelectorAll(
      `button:not(.MuiPickersCalendarHeader-iconButton):not(.MuiPickersDay-dayDisabled):not(.MuiPickersDay-hidden) span p`
    );
    const firstMonthDays = monthsContainer.current.querySelectorAll(
      `div.first-month button:not(.MuiPickersCalendarHeader-iconButton):not(.MuiPickersDay-dayDisabled):not(.MuiPickersDay-hidden) span p`
    );
    const secondMonthDays = monthsContainer.current.querySelectorAll(
      `div.second-month button:not(.MuiPickersCalendarHeader-iconButton):not(.MuiPickersDay-dayDisabled):not(.MuiPickersDay-hidden) span p`
    );
    const firstHeaderWithMonthAndYear = monthsContainer.current.querySelector(
      `div.first-month .MuiPickersCalendarHeader-switchHeader p.MuiTypography-root`
    );
    const secondHeaderWithMonthAndYear = monthsContainer.current.querySelector(
      `div.second-month .MuiPickersCalendarHeader-switchHeader p.MuiTypography-root`
    );
    if (firstHeaderWithMonthAndYear === null || secondHeaderWithMonthAndYear === null) return null;

    return {
      selectedDays,
      allDays,
      firstMonthDays,
      secondMonthDays,
      firstHeaderWithMonthAndYear,
      secondHeaderWithMonthAndYear,
    };
  };

  const changeDate = (date: CalendarDate) => {
    setRangePickerDates((prev) => {
      if (prev.firstDate !== null && prev.secondDate === null) return { firstDate: prev.firstDate, secondDate: date };
      return { firstDate: date, secondDate: null };
    });
  };

  return isVisible ? (
    <Wrapper {...wrapperProps} className="date-range-picker calendar-base" style={{ color: theme.text.main }}>
      <div className="months-wrapper" ref={monthsContainer}>
        <div className="first-month" onMouseOver={(e) => handleMouseOver(e, 'firstMonth')} onFocus={() => {}}>
          {firstDatePicker.titleChildren ? <Title {...firstDatePicker.titleProps}>{firstDatePicker.titleChildren}</Title> : null}
          <StyledDatePicker
            {...omit(firstDatePicker, 'title', 'variant', 'maxDate', 'onMonthChange', 'onChange', 'value')}
            maxDate={firstMonthMaxDate}
            value={firstDatePickerDate}
            onChange={(date) => changeDate(date)}
            onMonthChange={(date) => setMonthChangeDate({ from: 'firstMonth', date })}
            disableToolbar
          />
        </div>
        <Divider orientation="vertical" className="divider" />
        <div className="second-month" onMouseOver={(e) => handleMouseOver(e, 'secondMonth')} onFocus={() => {}}>
          {secondDatePicker.titleChildren ? (
            <Title {...secondDatePicker.titleProps}>{secondDatePicker.titleChildren}</Title>
          ) : null}
          <StyledDatePicker
            {...omit(secondDatePicker, 'title', 'variant', 'minDate', 'onMonthChange', 'onChange', 'value')}
            minDate={secondMonthMinDate}
            value={secondDatePickerDate}
            onChange={(date) => changeDate(date)}
            onMonthChange={(date) => setMonthChangeDate({ from: 'secondMonth', date })}
            disableToolbar
          />
        </div>
      </div>
      {buttonCancel || buttonSave ? (
        <div className="buttons-wrapper">
          {buttonCancel ? <Button {...buttonCancel} /> : null}
          {buttonSave ? <Button {...buttonSave} /> : null}
        </div>
      ) : null}
    </Wrapper>
  ) : null;
};
