/**
 * TODO: 国际化
 */
import React from 'react'
import isEqual from 'react-fast-compare'
import classNames from 'classnames'
import omit from 'lodash/omit'
import NoticeBar from '../notice-bar'
import { CalendarEventList, CalendarEvent } from './type'
import { DateDescList, genDates, isSameDay, isToday, inMonth } from './utils'

export interface SingleMonthProps {
  date: Date
  showOutOfMonthDay?: boolean
  showLunar?: boolean
  showHoliday?: boolean
  showGetEventError?: boolean
  currentSelect?: Date
  onSelect?: (date: Date) => void
  // 消除错误信息
  onSuppressError?: () => void
  // 触发获取事件
  onGetEventsForMonth?: (date: Date) => Promise<void>
  // 当月的事件列表, 数组类型
  events?: { [date: string]: any[] | undefined }
  legalHolidayEvents?: CalendarEventList
  style?: React.CSSProperties
}

interface State {
  dateList: DateDescList
  error?: Error
}

export default class SingleMonth extends React.Component<
  SingleMonthProps,
  State
> {
  public static defaultProps = {
    showLunar: true,
    showHoliday: true,
    showGetEventError: true,
  }

  public state: State = {
    dateList: genDates(this.props.date),
  }

  public componentDidMount() {
    this.initialEvents()
  }

  public shouldComponentUpdate(nextProps: SingleMonthProps, nextState: State) {
    const omitList = [
      'onSelect',
      'onSuppressError',
      'onGetEventsForMonth',
      'style',
      'currentSelect',
    ]

    if (
      this.props.currentSelect !== nextProps.currentSelect &&
      nextProps.currentSelect &&
      inMonth(nextProps.date, nextProps.currentSelect)
    ) {
      return true
    }

    if (
      !isEqual(this.state, nextState) ||
      !isEqual(omit(this.props, omitList), omit(nextProps, omitList))
    ) {
      return true
    }

    return false
  }

  public componentDidUpdate(prevProps: SingleMonthProps) {
    if (prevProps.date !== this.props.date) {
      this.setState({ dateList: genDates(this.props.date) })
    }
  }

  public render() {
    const { style, showGetEventError, onSuppressError } = this.props
    return (
      <main className="jm-calendar__body" style={style}>
        <div className="jm-calendar__weeks">{this.renderDays()}</div>
        {!!this.state.error && showGetEventError && (
          <NoticeBar
            action={
              <>
                <span onClick={this.initialEvents}>重试 </span>
                <span className="retry" onClick={onSuppressError}>
                  忽略
                </span>
              </>
            }
          >
            {this.state.error.message}
          </NoticeBar>
        )}
      </main>
    )
  }

  private renderDays() {
    const {
      showOutOfMonthDay,
      currentSelect = new Date(),
      showLunar,
      showHoliday,
    } = this.props
    const dateList = this.state.dateList
    const rows = dateList.length / 7
    const weeks: React.ReactNode[] = []
    for (let i = 0; i < rows; i++) {
      weeks.push(
        <div className="jm-calendar__week" key={i}>
          {dateList.slice(i * 7, (i + 1) * 7).map(i => {
            const dateStr = `${i.time.getFullYear()}-${i.time.getMonth() +
              1}-${i.time.getDate()}`
            const events = this.props.events && this.props.events[dateStr]
            const legalHolidayEvents =
              this.props.legalHolidayEvents &&
              this.props.legalHolidayEvents[dateStr]
            return (
              <div className="jm-calendar__day" key={i.id}>
                {!showOutOfMonthDay && (i.isPass || i.isFuture) ? null : (
                  <div
                    className={classNames(
                      {
                        weekday: i.isWeekDay,
                        active: isSameDay(currentSelect, i.time),
                        today: isToday(i.time),
                        'out-of-month': i.isFuture || i.isPass,
                        'has-event': events && events.length,
                      },
                      'jm-calendar__day-inner',
                    )}
                    onClick={this.handleSelect(i.time)}
                  >
                    {!!legalHolidayEvents &&
                      legalHolidayEvents.length &&
                      this.renderLegalHoliday(legalHolidayEvents[0])}
                    <div className="jm-calendar__day-number">{i.value}</div>
                    {showHoliday && !!i.holiday ? (
                      <div className="jm-calendar__day-desc">{i.holiday}</div>
                    ) : (
                      showLunar &&
                      !!i.lunar && (
                        <div className="jm-calendar__day-desc">{i.lunar}</div>
                      )
                    )}
                  </div>
                )}
              </div>
            )
          })}
        </div>,
      )
    }

    return weeks
  }

  private renderLegalHoliday(legalHolidayEvent?: CalendarEvent) {
    return (
      legalHolidayEvent && (
        <div
          className="jm-calendar__legal-holiday"
          style={{
            color: legalHolidayEvent.color || 'red',
          }}
        >
          {legalHolidayEvent.content}
        </div>
      )
    )
  }

  private initialEvents = async () => {
    if (this.props.onGetEventsForMonth == null) {
      return
    }
    try {
      this.setState({ error: undefined })
      await this.props.onGetEventsForMonth(this.props.date)
    } catch (error) {
      this.setState({ error })
    }
  }

  private handleSelect = (date: Date) => () => {
    if (this.props.onSelect) {
      this.props.onSelect(date)
    }
  }
}
