/**
 * Copyright (c) 2022 - present TinyVue Authors.
 * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
 *
 * Use of this source code is governed by an MIT-style license.
 *
 * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
 * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
 *
 */

import type {
  IDatePickerColumn,
  IDatePickerConstants,
  IDatePickerOriginColumn,
  IDatePickerProps,
  IDatePickerRenderlessParams,
  IDatePickerState
} from '@/types'

export const getMonthEndDay =
  (constants: IDatePickerConstants) =>
  (year: number, month: number): number =>
    constants.MonthDay - new Date(year, month - 1, constants.MonthDay).getDate()

export const getTrueValue = (value: string): number => {
  if (!value) {
    return 0
  }

  while (isNaN(parseInt(value, 10))) {
    if (value.length > 1) {
      value = value.slice(1)
    } else {
      return 0
    }
  }

  return parseInt(value, 10)
}

export const getBoundary =
  ({ api, constants, props }: Pick<IDatePickerRenderlessParams, 'api' | 'constants' | 'props'>) =>
  ({
    type,
    value: val
  }: {
    type: string
    value: Date
  }): {
    [x: string]: number
  } => {
    const boundary = props[`${type}${constants.CapDate}`]
    const year = boundary.getFullYear()
    let month = 1
    let date = 1
    let hour = 0
    let minute = 0

    if (type === constants.Max) {
      month = constants.TotalMonth
      date = api.getMonthEndDay(val.getFullYear(), val.getMonth() + 1)
      hour = constants.Hours
      minute = constants.Minutes
    }

    if (val.getFullYear() === year) {
      month = boundary.getMonth() + 1
      if (val.getMonth() + 1 === month) {
        date = boundary.getDate()
        if (val.getDate() === date) {
          hour = boundary.getHours()
          if (val.getHours() === hour) {
            minute = boundary.getMinutes()
          }
        }
      }
    }

    return {
      [`${type}${constants.CapYear}`]: year,
      [`${type}${constants.CapMonth}`]: month,
      [`${type}${constants.CapDate}`]: date,
      [`${type}${constants.CapHour}`]: hour,
      [`${type}${constants.CapMinute}`]: minute
    }
  }

export const updateInnerValue =
  ({
    api,
    constants,
    props,
    refs,
    state
  }: Pick<IDatePickerRenderlessParams, 'api' | 'constants' | 'props' | 'refs' | 'state'>) =>
  () => {
    const indexes = refs.picker && refs.picker.getIndexes()

    const getValue = (index) => {
      const { values } = state.originColumns[index]
      return getTrueValue(values[indexes[index]])
    }

    const year = getValue(0)
    const month = getValue(1)
    const maxDate = api.getMonthEndDay(year, month)

    let date
    if (props.type === constants.YearMonth) {
      date = 1
    } else {
      date = getValue(2)
    }

    date = date > maxDate ? maxDate : date

    let hour = 0
    let minute = 0

    if (props.type === constants.DateTime) {
      hour = getValue(3)
      minute = getValue(4)
    }

    const value = new Date(year, month - 1, date, hour, minute)

    state.innerValue = api.formatValue(value)
  }

export const formatValue =
  (props: IDatePickerProps) =>
  (value: number): Date => {
    if (!Object.prototype.toString.call(value) === '[object Date]' && !isNaN(value.getTime())) {
      value = props.minDate
    }

    value = Math.max(value, props.minDate.getTime())
    value = Math.min(value, props.maxDate.getTime())

    return new Date(value)
  }

export const onChange =
  ({ api, emit, refs, nextTick }: Pick<IDatePickerRenderlessParams, 'api' | 'emit' | 'refs' | 'nextTick'>) =>
  () => {
    api.updateInnerValue()

    nextTick(() => {
      nextTick(() => {
        emit('change', refs.picker)
        document.body.style.overflow = ''
      })
    })
  }

export const padZero = (num: number, targetLength = 2): string => {
  let str = String(num)

  while (str.length < targetLength) {
    str = '0' + str
  }

  return str
}

export const updateColumnValue =
  ({
    constants,
    nextTick,
    props,
    refs,
    state
  }: Pick<IDatePickerRenderlessParams, 'constants' | 'nextTick' | 'props' | 'refs' | 'state'>) =>
  () => {
    const value = state.innerValue
    const { formatter } = props

    let values = [
      formatter('year', `${value.getFullYear()}`),
      formatter('month', padZero(value.getMonth() + 1)),
      formatter('day', padZero(value.getDate()))
    ]

    if (props.type === constants.DateTime) {
      values.push(formatter('hour', padZero(value.getHours())), formatter('minute', padZero(value.getMinutes())))
    }

    if (props.type === constants.YearMonth) {
      values = values.slice(0, 2)
    }

    nextTick(() => {
      refs.picker.setValues(values)
    })
  }

export const getRanges =
  ({ api, constants, props, state }) =>
  (): {
    type: string
    range: number[]
  }[] => {
    const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = api.getBoundary({
      type: constants.Max,
      value: state.innerValue
    })

    const { minYear, minDate, minMonth, minHour, minMinute } = api.getBoundary({
      type: constants.Min,
      value: state.innerValue
    })

    const result: {
      type: string
      range: number[]
    }[] = [
      {
        type: constants.Year,
        range: [minYear, maxYear]
      },
      {
        type: 'month',
        range: [minMonth, maxMonth]
      },
      {
        type: constants.Day,
        range: [minDate, maxDate]
      },
      {
        type: constants.Hour,
        range: [minHour, maxHour]
      },
      {
        type: constants.Minute,
        range: [minMinute, maxMinute]
      }
    ]

    if (props.type === constants.Date) {
      result.splice(3, 2)
    }
    if (props.type === constants.YearMonth) {
      result.splice(2, 3)
    }
    return result
  }

export function times(n: number, iteratee: (index: number) => string): number[] {
  let index = -1
  const result = Array(n)

  while (++index < n) {
    result[index] = iteratee(index)
  }

  return result
}

export const getOriginColumns = (state: IDatePickerState) => (): IDatePickerOriginColumn[] =>
  state.ranges.map(({ type, range: rangeArr }) => {
    let values = times(rangeArr[1] - rangeArr[0] + 1, (index) => {
      const value = padZero(rangeArr[0] + index)
      return value
    })

    return {
      type,
      values
    }
  })

export const getColumns =
  ({ props, state }: Pick<IDatePickerRenderlessParams, 'props' | 'state'>) =>
  (): IDatePickerColumn[] =>
    state.originColumns.map((column) => ({
      values: column.values.map((value) => props.formatter(column.type, value))
    }))

export const onConfirm =
  ({ api, emit, state }: Pick<IDatePickerRenderlessParams, 'api' | 'emit' | 'state'>) =>
  () => {
    state.visible = false
    emit('confirm', state.innerValue)
    emit('update:modelValue', state.innerValue)
    emit('update:visible', state.visible)
    document.body.style.overflow = ''
    state.displayValue = api.getDisplayValue()
    state.clearable = false
  }

export const onCancel =
  ({ emit, state }: Pick<IDatePickerRenderlessParams, 'emit' | 'state'>) =>
  () => {
    state.visible = false
    emit('cancel')
    emit('update:visible', state.visible)
    document.body.style.overflow = ''
  }

export const getDisplayValue =
  ({ constants, DATE, props, state }: Pick<IDatePickerRenderlessParams, 'constants' | 'DATE' | 'props' | 'state'>) =>
  (): string => {
    const format = function (value: Date, fmt: string): string {
      const o = {
        'M+': value.getMonth() + 1,
        'd+': value.getDate(),
        'h+': value.getHours(),
        'm+': value.getMinutes(),
        's+': value.getSeconds(),
        'q+': Math.floor((value.getMonth() + 3) / 3),
        'S': value.getMilliseconds()
      }

      if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, String(value.getFullYear()).substr(4 - RegExp.$1.length))
      }
      for (let k in o) {
        if (new RegExp('(' + k + ')').test(fmt)) {
          fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(String(o[k]).length))
        }
      }
      return fmt
    }

    return format(state.innerValue, props.type === constants.DateTime ? DATE.Datetime : DATE.Date)
  }

export const hookMounted =
  ({
    constants,
    parent,
    refs,
    nextTick
  }: Pick<IDatePickerRenderlessParams, 'constants' | 'parent' | 'refs' | 'nextTick'>) =>
  () => {
    nextTick(() => {
      parent.$emit(constants.HookMounted, refs.refrence.$el)
    })
  }

export const showPickerAndLockScroll =
  ({ constants, state }: Pick<IDatePickerRenderlessParams, 'constants' | 'state'>) =>
  () => {
    state.visible = true
    document.body.style.overflow = constants.Hidden
    state.isReadonly = true
  }

export const clearDisplayValue = (state: IDatePickerState) => () => {
  state.displayValue = ''
  state.clearable = true
}