/* eslint-disable no-fallthrough */
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import DayCell, { rangeShape } from './DayCell.js';
import {
  format,
  startOfDay,
  endOfDay,
  startOfWeek,
  endOfWeek,
  isBefore,
  isSameDay,
  isAfter,
  isWeekend,
  isWithinInterval,
  eachDayOfInterval
} from 'date-fns';
import { getMonthDisplayRange } from '../utils';

function renderWeekdays(styles, dateOptions) {
  const now = new Date();
  return (
    <div className={styles.weekDays}>
      {eachDayOfInterval({
        start: startOfWeek(now, dateOptions),
        end: endOfWeek(now, dateOptions)
      }).map((day, i) => (
        <span className={styles.weekDay} key={i}>
          {format(day, 'ddd', dateOptions)}
        </span>
      ))}
    </div>
  );
}

class Month extends PureComponent {
  render() {
    const now = new Date();
    const { displayMode, focusedRange, drag, styles, disabledDates } = this.props;
    const minDate = this.props.minDate && startOfDay(this.props.minDate);
    const maxDate = this.props.maxDate && endOfDay(this.props.maxDate);
    const monthDisplay = getMonthDisplayRange(this.props.month, this.props.dateOptions);
    let ranges = this.props.ranges;
    if (displayMode === 'dateRange' && drag.status) {
      let { startDate, endDate } = drag.range;
      ranges = ranges.map((range, i) => {
        if (i !== focusedRange[0]) return range;
        return {
          ...range,
          startDate,
          endDate
        };
      });
    }
    const showPreview = this.props.showPreview && !drag.disablePreview;
    return (
      <div className={styles.month} style={this.props.style}>
        {this.props.showMonthName ? (
          <div className={styles.monthName}>
            {format(this.props.month, this.props.monthDisplayFormat, this.props.dateOptions)}
          </div>
        ) : null}
        {this.props.showWeekDays && renderWeekdays(styles, this.props.dateOptions)}
        <div className={styles.days} onMouseLeave={this.props.onMouseLeave}>
          {eachDayOfInterval({ start: monthDisplay.start, end: monthDisplay.end }).map((day, index) => {
            const isStartOfMonth = isSameDay(day, monthDisplay.startDateOfMonth);
            const isEndOfMonth = isSameDay(day, monthDisplay.endDateOfMonth);
            const isOutsideMinMax = (minDate && isBefore(day, minDate)) || (maxDate && isAfter(day, maxDate));
            const isDisabledSpecifically = disabledDates.some((disabledDate) => isSameDay(disabledDate, day));
            return (
              <DayCell
                {...this.props}
                ranges={ranges}
                day={day}
                preview={showPreview ? this.props.preview : null}
                isWeekend={isWeekend(day, this.props.dateOptions)}
                isToday={isSameDay(day, now)}
                isStartOfWeek={isSameDay(day, startOfWeek(day, this.props.dateOptions))}
                isEndOfWeek={isSameDay(day, endOfWeek(day, this.props.dateOptions))}
                isStartOfMonth={isStartOfMonth}
                isEndOfMonth={isEndOfMonth}
                key={index}
                disabled={isOutsideMinMax || isDisabledSpecifically}
                isPassive={
                  !isWithinInterval(day, {
                    start: monthDisplay.startDateOfMonth,
                    end: monthDisplay.endDateOfMonth
                  })
                }
                styles={styles}
                onMouseDown={this.props.onDragSelectionStart}
                onMouseUp={this.props.onDragSelectionEnd}
                onMouseEnter={this.props.onDragSelectionMove}
                dragRange={drag.range}
                drag={drag.status}
              />
            );
          })}
        </div>
      </div>
    );
  }
}

Month.defaultProps = {};

Month.propTypes = {
  style: PropTypes.object,
  styles: PropTypes.object,
  month: PropTypes.object,
  drag: PropTypes.object,
  dateOptions: PropTypes.object,
  disabledDates: PropTypes.array,
  preview: PropTypes.shape({
    startDate: PropTypes.object,
    endDate: PropTypes.object
  }),
  showPreview: PropTypes.bool,
  displayMode: PropTypes.oneOf(['dateRange', 'date']),
  minDate: PropTypes.object,
  maxDate: PropTypes.object,
  ranges: PropTypes.arrayOf(rangeShape),
  focusedRange: PropTypes.arrayOf(PropTypes.number),
  onDragSelectionStart: PropTypes.func,
  onDragSelectionEnd: PropTypes.func,
  onDragSelectionMove: PropTypes.func,
  onMouseLeave: PropTypes.func,
  monthDisplayFormat: PropTypes.string,
  showWeekDays: PropTypes.bool,
  showMonthName: PropTypes.bool
};

export default Month;
