import React from 'react'
import MultiPicker from '@gdjiami/rmc-picker/es/MultiPicker'
import Picker from '@gdjiami/rmc-picker/es/Picker'
import { Today, isSameYear, isSameMonth, getLastDateOfMonth, isLeap } from '../calendar'

export interface DatePickerInnerProps {
  value?: Date
  onChange?: (value: Date) => void
  disabledBefore?: Date
  disabledAfter?: Date
}

type YearList = number[]
type MonthList = { before: number[]; after: number[]; normal: number[] }
type DayList = {
  before: number[]
  after: number[]
  // 闰二月为leap-2
  normal: { [key: string]: number[] }
}

const defaultDisabledBefore = new Date(0)
const defaultDisabledAfter = new Date(2100, 1, 1)
const monthList: number[] = []
for (let i = 1; i <= 12; i++) {
  monthList.push(i)
}

interface DatePickerInnerState {
  yearList: YearList
  monthList: MonthList
  dayList: DayList
  normalizedValue: [number, number, number]
}

export default class DatePickerInner extends React.PureComponent<DatePickerInnerProps, DatePickerInnerState> {
  public state: DatePickerInnerState

  public constructor(props: DatePickerInnerProps) {
    super(props)
    const value = this.props.value || Today
    const { disabledBefore, disabledAfter } = this.props
    this.state = {
      yearList: genYearList(value, disabledBefore, disabledAfter),
      monthList: genMonthList(value, disabledBefore, disabledAfter),
      dayList: genDayList(value, disabledBefore, disabledAfter),
      normalizedValue: [value.getFullYear(), value.getMonth() + 1, value.getDate()],
    }
  }

  public componentDidUpdate(prevProps: DatePickerInnerProps, prevState: DatePickerInnerState) {
    if (
      this.props.value &&
      (this.props.disabledBefore !== prevProps.disabledBefore || this.props.disabledAfter !== prevProps.disabledAfter)
    ) {
      const { value, disabledBefore, disabledAfter } = this.props
      this.setState({
        yearList: genYearList(value, disabledBefore, disabledAfter),
        monthList: genMonthList(value, disabledBefore, disabledAfter),
        dayList: genDayList(value, disabledBefore, disabledAfter),
      })
    }

    if (this.props.value && this.props.value !== prevProps.value) {
      const value = this.props.value
      this.setState({
        normalizedValue: [value.getFullYear(), value.getMonth() + 1, value.getDate()],
      })
    }
  }

  public render() {
    const { yearList, normalizedValue } = this.state
    return (
      <>
        <MultiPicker
          className="jm-date-picker__body"
          selectedValue={normalizedValue}
          onValueChange={this.handleSelectChange}
        >
          <Picker className="jm-date-picker__date">
            {yearList.map(i => (
              <Picker.Item value={i} key={i}>
                {i}年
              </Picker.Item>
            ))}
          </Picker>
          <Picker className="jm-date-picker__date">{this.renderMonth()}</Picker>
          <Picker className="jm-date-picker__date">{this.renderDay()}</Picker>
        </MultiPicker>
      </>
    )
  }

  private renderMonth() {
    const { disabledAfter, disabledBefore, value = Today } = this.props
    const { monthList } = this.state
    const list =
      disabledBefore && isSameYear(disabledBefore, value)
        ? monthList.before
        : disabledAfter && isSameYear(disabledAfter, value)
        ? monthList.after
        : monthList.normal
    return list.map(i => (
      <Picker.Item value={i} key={i}>
        {i}月
      </Picker.Item>
    ))
  }

  private renderDay() {
    const { disabledAfter, disabledBefore, value = Today } = this.props
    const { dayList } = this.state
    const list =
      disabledBefore && isSameMonth(disabledBefore, value)
        ? dayList.before
        : disabledAfter && isSameMonth(disabledAfter, value)
        ? dayList.after
        : dayList.normal[value.getMonth() === 1 && isLeap(value.getFullYear()) ? 'leap-2' : value.getMonth() + 1]
    return list.map(i => (
      <Picker.Item value={i} key={i}>
        {i}日
      </Picker.Item>
    ))
  }

  private handleSelectChange = ([year, month, date]: [number, number, number]) => {
    const value = new Date(this.props.value || 0)
    value.setFullYear(year)
    value.setDate(1)
    value.setMonth(month - 1)
    value.setDate(Math.min(date, getLastDateOfMonth(value)))

    if (this.props.onChange) {
      this.props.onChange(value)
    }
  }
}

function genYearList(
  date: Date,
  disabledBefore: Date = defaultDisabledBefore,
  disabledAfter: Date = defaultDisabledAfter,
) {
  const list: YearList = [date.getFullYear()]
  let prev = new Date(date)
  let next = new Date(date)
  let prevFinished: boolean = false
  let nextFinished: boolean = false

  for (let i = 1; i <= 100; i++) {
    if (!prevFinished) {
      prev = new Date(prev)
      prev.setFullYear(prev.getFullYear() - 1)
      if (disabledBefore.getFullYear() > prev.getFullYear()) {
        prevFinished = true
      } else {
        list.unshift(prev.getFullYear())
      }
    }

    if (!nextFinished) {
      next = new Date(next)
      next.setFullYear(next.getFullYear() + 1)
      if (next.getFullYear() > disabledAfter.getFullYear()) {
        nextFinished = true
      } else {
        list.push(next.getFullYear())
      }
    }
  }

  return list
}

function genMonthList(
  date: Date,
  disabledBefore: Date = defaultDisabledBefore,
  disabledAfter: Date = defaultDisabledAfter,
) {
  let before: number[] = []
  let after: number[] = []
  const normal: number[] = []
  const sameYear = isSameYear(disabledBefore, disabledAfter)

  for (let i = disabledBefore.getMonth() + 1, l = sameYear ? disabledAfter.getMonth() + 1 : 12; i <= l; i++) {
    before.push(i)
  }

  if (sameYear) {
    after = before
  } else {
    for (let i = 1; i <= disabledAfter.getMonth() + 1; i++) {
      after.push(i)
    }
  }

  for (let i = 1; i <= 12; i++) {
    normal.push(i)
  }

  return { before, after, normal }
}

function genDayList(
  date: Date,
  disabledBefore: Date = defaultDisabledBefore,
  disabledAfter: Date = defaultDisabledAfter,
) {
  let before: number[] = []
  let after: number[] = []
  let normal: { [key: string]: number[] } = {}
  let sameMonth = isSameMonth(disabledAfter, disabledBefore)

  for (
    let i = disabledBefore.getDate(), l = sameMonth ? disabledAfter.getDate() : getLastDateOfMonth(disabledBefore);
    i <= l;
    i++
  ) {
    before.push(i)
  }

  if (sameMonth) {
    after = before
  } else {
    for (let i = 1; i <= disabledAfter.getDate(); i++) {
      after.push(i)
    }
  }

  const tmpDate = new Date(2019, 0, 1)
  for (let i = 0; i < 12; i++) {
    tmpDate.setMonth(i + 1)
    tmpDate.setDate(0)
    const end = tmpDate.getDate()
    tmpDate.setDate(1)
    const dayList: number[] = (normal[i + 1] = [])
    for (let j = 1; j <= end; j++) {
      dayList.push(j)
    }
  }

  normal['leap-2'] = []
  for (let i = 1; i <= 29; i++) {
    normal['leap-2'].push(i)
  }

  return {
    before,
    after,
    normal,
  }
}
