/* 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 {
  CalendarDate,
  RangePickerDatesType,
  DatePickerProps,
  DatePickerWithRangeProps,
  DatePickerWithoutRangeProps,
  HtmlElementsType,
} from '../types.d';
import { Button } from '../../Button';
import { Title } from '../../Title';
import { Wrapper } from '../../Wrapper';
import { useTheme } from '../../Theme';

const DatePickerWithRange: FunctionComponent<DatePickerWithRangeProps> = (props) => {
  const theme = useTheme();
  const { initDate, onChange, isVisible } = props;
  const dateStringFormat = 'DD MMMM YYYY';

  const [monthChangeDate, setMonthChangeDate] = useState<CalendarDate>(moment(initDate).startOf('month'));

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

  const monthContainer = useRef<HTMLDivElement>(null);

  useEffect(() => {
    setMonthChangeDate(moment(initDate).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]);

  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) => {
    if (rangePickerDates.firstDate === null || rangePickerDates.secondDate !== null) return;
    const elements = getHtmlElements();
    if (elements === null) return;
    const { firstMonthDays, firstHeaderWithMonthAndYear } = elements;

    const firstDateString = moment(rangePickerDates.firstDate).format(dateStringFormat);
    const currentDateString = `${(event.target as HTMLElement).innerText} ${firstHeaderWithMonthAndYear.innerHTML}`;

    const firstDateValue = getDateValue(firstDateString);
    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}`);
    });
  };

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

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

    const firstDateValue = getDateValue(firstDateString);
    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}`);
      }
    });
  };

  const getHtmlElements = (): HtmlElementsType | null => {
    if (monthContainer === null || monthContainer.current === null) return null;

    const selectedDays = monthContainer.current.querySelectorAll('button span.MuiPickersDay-daySelected');
    const allDays = monthContainer.current.querySelectorAll(
      `button:not(.MuiPickersCalendarHeader-iconButton):not(.MuiPickersDay-dayDisabled):not(.MuiPickersDay-hidden) span p`
    );
    const firstMonthDays = monthContainer.current.querySelectorAll(
      `div.first-month button:not(.MuiPickersCalendarHeader-iconButton):not(.MuiPickersDay-dayDisabled):not(.MuiPickersDay-hidden) span p`
    );

    const firstHeaderWithMonthAndYear = monthContainer.current.querySelector(
      `div.first-month .MuiPickersCalendarHeader-switchHeader p.MuiTypography-root`
    );

    if (firstHeaderWithMonthAndYear === null) return null;

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

  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 (
    <div className="month-wrapper" ref={monthContainer}>
      <div className="first-month" onMouseOver={handleMouseOver} onFocus={() => {}}>
        <StyledDatePicker
          {...omit(props, 'variant', 'onMonthChange', 'onChange', 'value')}
          value={monthChangeDate}
          onChange={(date) => changeDate(date)}
          onMonthChange={(date) => setMonthChangeDate(date)}
          disableToolbar
        />
      </div>
    </div>
  );
};

export const DatePicker: FunctionComponent<DatePickerProps> = (props) => {
  const theme = useTheme();
  const { wrapperProps, isRange, params, children, titleProps, titleChildren, isVisible, buttonCancel, buttonSave } = props;

  const renderCalendar = () => {
    return isRange ? (
      <DatePickerWithRange {...(params as DatePickerWithRangeProps)} isVisible={isVisible} />
    ) : (
      <StyledDatePicker {...(params as DatePickerWithoutRangeProps)} disableToolbar />
    );
  };

  return isVisible ? (
    <Wrapper
      {...wrapperProps}
      className={isRange ? 'date-range-picker calendar-base' : 'date-picker calendar-base'}
      style={{ color: theme.text.main }}
    >
      {titleChildren ? <Title {...titleProps}>{titleChildren}</Title> : null}
      {renderCalendar()}
      {children ? <div className="calendar-children">{children}</div> : null}
      {buttonCancel || buttonSave ? (
        <div className="buttons-wrapper">
          {buttonCancel ? <Button {...buttonCancel} /> : null}
          {buttonSave ? <Button {...buttonSave} /> : null}
        </div>
      ) : null}
    </Wrapper>
  ) : null;
};
