import { formatDate } from '@transferwise/formatting';
import { PureComponent } from 'react';
import { injectIntl, type WrappedComponentProps } from 'react-intl';

import { getDayNames, isWithinRange } from '../../../common/dateUtils';
import { getFocusableTime } from '../../getFocusableTime/getFocusableTime';
import { getStartOfDay } from '../../getStartOfDay';
import TableLink from '../../tableLink';

const SHORT_DAY_FORMAT: Intl.DateTimeFormatOptions = { day: 'numeric' };

interface DayCalendarTableProps extends WrappedComponentProps {
  selectedDate: Date | null;
  min: Date | null;
  max: Date | null;
  viewMonth: number;
  viewYear: number;
  onSelect: (date: Date) => void;
}

class DayCalendarTable extends PureComponent<DayCalendarTableProps> {
  getTableStructure = () => {
    const { viewMonth, viewYear } = this.props;
    let firstDayOfMonth = new Date(viewYear, viewMonth, 1).getDay();
    // JS Sunday is 0, we're setting it last
    if (firstDayOfMonth === 0) {
      firstDayOfMonth = 7;
    }
    const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();

    let week: number[] = [];
    const weeks: number[][] = [];
    let i;

    // Pad first week
    for (i = 1; i < firstDayOfMonth; i += 1) {
      week.push(-1);
    }
    // Fill in days
    for (i = 1; i <= daysInMonth; i += 1) {
      week.push(i);
      if ((firstDayOfMonth + i - 1) % 7 === 0) {
        weeks.push(week);
        week = [];
      }
    }
    if (week.length > 0) {
      // Pad last week
      for (i = week.length; i < 7; i += 1) {
        week.push(-1);
      }
      weeks.push(week);
    }
    return weeks;
  };

  days = getDayNames(this.props.intl.locale, 'short');
  daysShort = getDayNames(this.props.intl.locale, 'narrow');
  daysLong = getDayNames(this.props.intl.locale, 'long');

  selectDay = (day: number) => {
    const { viewMonth, viewYear, onSelect } = this.props;
    onSelect(new Date(viewYear, viewMonth, day));
  };

  isDisabled = (day: number) => {
    if (day < 0) {
      return true;
    }
    const { min, max, viewMonth, viewYear } = this.props;
    const date = new Date(viewYear, viewMonth, day);

    return !isWithinRange(date, min, max);
  };

  isActive = (day: number) => {
    const { selectedDate, viewMonth, viewYear } = this.props;
    return !!(selectedDate && Number(new Date(viewYear, viewMonth, day)) === Number(selectedDate));
  };

  isToday = (day: number) => {
    const { viewMonth, viewYear } = this.props;
    return Number(getStartOfDay(new Date())) === Number(new Date(viewYear, viewMonth, day));
  };

  getAutofocusDay = (weeks: number[][]) => {
    const days = weeks.flatMap((week) => week);
    return getFocusableTime({
      isActive: this.isActive,
      isNow: this.isToday,
      isDisabled: this.isDisabled,
      timeSpan: days,
    });
  };

  render() {
    const { viewMonth, viewYear, intl } = this.props;
    const weeks = this.getTableStructure();
    const autoFocusDay = this.getAutofocusDay(weeks);
    return (
      <table className="table table-condensed table-bordered tw-date-lookup-calendar m-b-0">
        <thead>
          <tr>
            {this.days.map((day, index) => (
              <th
                key={day}
                className={
                  index > 4
                    ? 'text-xs-center np-text-body-default'
                    : 'text-xs-center np-text-body-default-bold'
                }
              >
                <span className="hidden-xs">
                  <abbr title={this.daysLong[index]}>{day.slice(0, 3)}</abbr>
                </span>
                <span className="visible-xs-inline-block">
                  <abbr title={this.daysLong[index]}>{this.daysShort[index].slice(0, 2)}</abbr>
                </span>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {/* eslint-disable react/no-array-index-key */}
          {weeks.map((week, weekIndex) => (
            <tr key={weekIndex}>
              {week.map((day, dayIndex) => (
                <td key={dayIndex} className={dayIndex > 4 ? 'weekend' : ''}>
                  {day !== -1 && (
                    <TableLink
                      item={day}
                      type="day"
                      title={formatDate(
                        new Date(viewYear, viewMonth, day),
                        intl.locale,
                        SHORT_DAY_FORMAT,
                      )}
                      autofocus={day === autoFocusDay}
                      longTitle={formatDate(new Date(viewYear, viewMonth, day), intl.locale)}
                      active={this.isActive(day)}
                      disabled={this.isDisabled(day)}
                      today={this.isToday(day)}
                      onClick={this.selectDay}
                    />
                  )}
                </td>
              ))}
            </tr>
          ))}
          {/* eslint-enable react/no-array-index-key */}
        </tbody>
      </table>
    );
  }
}

export default injectIntl(DayCalendarTable);
