import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import classnames from 'classnames';

dayjs.extend(customParseFormat);

const propTypes = {
  defaultClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),

  /**
   * Class name for the DatePicker.
   */
  className: PropTypes.string,

  /**
   * The custom tag for the root of the DatePicker.
   */
  tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),

  /**
   * The default date to be selected in the calendar.
   */
  selected: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(dayjs)]),

  /**
   * Set the new date value to be selected in the calendar.
   */
  onChange: PropTypes.func,

  /**
   * Set the date format.
   */
  defaultFormat: PropTypes.string,
};

const defaultProps = {
  defaultClassName: 'sui-o-date-picker',
  className: '',
  tag: 'div',
  selected: dayjs(),
  onChange: () => {},
  defaultFormat: 'YYYY/MM/DD',
};

export const DatePicker = ({
  defaultClassName,
  className,
  tag: Tag,
  selected,
  onChange,
  defaultFormat,
}) => {
  const [selectedDate, setSelectedDate] = useState(
    dayjs(selected, defaultFormat, true).isValid() ? dayjs(selected, defaultFormat) : dayjs(),
  );

  const classes = classnames(defaultClassName, className);
  const nowDate = dayjs();
  const calendarHeader = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

  const handleDateChange = (date) => {
    setSelectedDate(date);
  };

  // Subtract 1 month from the "selectedDate" and set the day to the 1st of that month
  const handlePreviousMonth = () => {
    const previousMonth = dayjs(selectedDate).subtract(1, 'month').date(1);
    setSelectedDate(previousMonth);
  };

  // Add 1 month to the "selectedDate" and set the day to the 1st of that month
  const handleNextMonth = () => {
    const nextMonth = dayjs(selectedDate).add(1, 'month').date(1);
    setSelectedDate(nextMonth);
  };

  const getCalendarDays = () => {
    const firstDayOfMonth = dayjs(selectedDate).startOf('month');
    const lastDayOfMonth = dayjs(selectedDate).endOf('month');
    const daysInMonth = lastDayOfMonth.date();
    const startingDay = firstDayOfMonth.day() - 1;
    const days = [];

    // Calculate the days of the previous month
    const prevMonth = dayjs(selectedDate).subtract(1, 'month').date(1);
    const prevMonthDays = prevMonth.endOf('month').date();

    // Add the days of the previous month
    for (let i = startingDay - 1; i >= 0; i -= 1) {
      days.push(prevMonth.date(prevMonthDays - i));
    }

    // Add the days of the current month
    for (let i = 1; i <= daysInMonth; i += 1) {
      days.push(firstDayOfMonth.date(i));
    }

    // Add the days of the next month
    for (let i = 1; days.length % 7 !== 0; i += 1) {
      days.push(dayjs(selectedDate).add(1, 'month').date(i));
    }

    return days;
  };

  const renderCalendarDays = () => {
    const calendarDays = [];
    const allDaysInMonth = getCalendarDays();
    const daysCount = allDaysInMonth.length;
    for (let i = 0; i < daysCount; i += 7) {
      const weekDays = allDaysInMonth.slice(i, i + 7);
      const weekRow = (
        <tr key={i}>
          {weekDays.map((day) => {
            const isToday = day.isSame(nowDate, 'day');
            const isSelected = day.isSame(selectedDate, 'day');
            const isCurrentMonth = day.month() === selectedDate.month();
            const style = {
              color: (!isSelected && isToday && '#fff') || (!isCurrentMonth ? '#a0b2ca' : ''),
              // eslint-disable-next-line no-nested-ternary
              background: (!isSelected && isToday && '#0172f5') || (isSelected ? '#e9ecf1' : ''),
              fontWeight: isSelected ? 'bold' : '',
            };

            return (
              <td
                key={day.valueOf()}
                role="gridcell"
                onClick={() => handleDateChange(day)}
                style={style}
                className="sui-o-date-picker__days"
              >
                {day.date()}
              </td>
            );
          })}
        </tr>
      );
      calendarDays.push(weekRow);
    }
    return calendarDays;
  };

  useEffect(() => {
    if (!selectedDate.isSame(selected) && dayjs(selected, defaultFormat, true).isValid()) {
      setSelectedDate(dayjs(selected, defaultFormat));
    }
  }, [selected]);

  // To prevent the parent of the date change
  useEffect(() => {
    onChange(selectedDate.format(defaultFormat));
  }, [selectedDate]);

  return (
    <Tag className={classes}>
      <div className="sui-g-grid as--no-wrap as--between sui-h-pb-md">
        <button type="button" onClick={handlePreviousMonth} className="sui-g-grid__item as--pull">&lt;</button>
        <span className="sui-g-grid__item as--auto">
          {selectedDate.format('MMMM YYYY')}
        </span>
        <button type="button" onClick={handleNextMonth} className="sui-g-grid__item as--push">&gt;</button>
      </div>

      <table role="grid">
        <thead>
          <tr>
            {calendarHeader.map((day) => (
              <th key={day}>{day}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {renderCalendarDays()}
        </tbody>
      </table>
    </Tag>
  );
};

DatePicker.propTypes = propTypes;
DatePicker.defaultProps = defaultProps;
