import type { ComponentPublicInstance, ExtractPropTypes, PropType, Ref } from 'vue'
import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
import { getType, isArray, isObj } from '../common/util'

export type ColumnItem = {
  [key: string]: any
  value?: string | number | boolean
  label?: string
  disabled?: boolean
}

export type PickerViewColumnChange = (
  pickerView: PickerViewInstance,
  selects: Record<string, any> | Record<string, any>[],
  index: number,
  reslove: () => void
) => void

export const pickerViewProps = {
  ...baseProps,
  /**
   * 加载状态
   */
  loading: makeBooleanProp(false),
  /**
   * 加载的颜色，只能使用十六进制的色值写法，且不能使用缩写
   */
  loadingColor: makeStringProp('#4D80F0'),
  /**
   * picker内部滚筒高
   */
  columnsHeight: makeNumberProp(217),
  /**
   * picker item的高度
   */
  itemHeight: makeNumberProp(35),
  /**
   * 选项对象中，value对应的 key
   */
  valueKey: makeStringProp('value'),
  /**
   * 选项对象中，展示的文本对应的 key
   */
  labelKey: makeStringProp('label'),
  /**
   * 是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件，1.2.25版本起提供，仅微信小程序和支付宝小程序支持。
   */
  immediateChange: makeBooleanProp(false),
  /**
   * 选中项，如果为多列选择器，则其类型应为数组
   */
  modelValue: {
    type: [String, Number, Boolean, Array<number>, Array<string>, Array<boolean>] as PropType<
      string | number | boolean | Array<number> | Array<string> | Array<boolean>
    >,
    default: '',
    required: true
  },
  /**
   * 选择器数据，可以为字符串数组，也可以为对象数组，如果为二维数组，则为多列选择器
   */
  columns: makeArrayProp<string | number | ColumnItem | Array<number> | Array<string> | Array<ColumnItem>>(),
  /**
   * 接收 pickerView 实例、选中项、当前修改列的下标、resolve 作为入参，根据选中项和列下标进行判断，通过 pickerView 实例暴露出来的 setColumnData 方法修改其他列的数据源。
   */
  columnChange: Function as PropType<PickerViewColumnChange>
}

export type PickerViewExpose = {
  getSelects: () => Record<string, any> | Record<string, any>[]
  getValues: () => string | string[]
  setColumnData: (columnIndex: number, data: Array<string | number | ColumnItem | Array<string | number | ColumnItem>>, rowIndex?: number) => void
  getColumnsData: () => Record<string, string>[][]
  getColumnData: (columnIndex: number) => Record<string, string>[]
  getColumnIndex: (columnIndex: number) => number
  getLabels: () => string[]
  getSelectedIndex: () => number[]
  resetColumns: (columns: (string | number | string[] | number[] | ColumnItem | ColumnItem[])[]) => void
}

export type PickerViewProps = ExtractPropTypes<typeof pickerViewProps>

export type PickerViewInstance = ComponentPublicInstance<PickerViewProps, PickerViewExpose>

/**
 * 格式化传入的列数据
 * 列数据统一格式化为二维数组
 * @param array 列数据
 * @param valueKey
 * @param labelKey
 * @returns
 */
export function formatArray(
  array: Array<string | number | ColumnItem | Array<string | number | ColumnItem>>,
  valueKey: string,
  labelKey: string
): ColumnItem[][] {
  let tempArray: Array<string | number | ColumnItem | Array<string | number | ColumnItem>> = isArray(array) ? array : [array]
  // 判断数组第一层的数据类型，如果存在多种类型，则抛错
  const firstLevelTypeList = new Set(array.map(getType))
  /**
   * 存在三种类型的合法数据
   * 1.数组是一维元素，所有元素都是原始值
   * 2.数组是一维元素，所有元素都是object
   * 3.数组是二维元素，二维元素可以是任意内容
   */
  if (firstLevelTypeList.size !== 1 && firstLevelTypeList.has('object')) {
    // 原始值和引用类型不用混用
    throw Error('The columns are correct')
  }
  /**
   * 简单处理，如果数组第一项不是数组则认为它是一个一维数组
   * 所以需要把一维的转成二维，这样方便统一处理
   */
  if (!isArray(array[0])) {
    tempArray = [tempArray as Array<string | number | ColumnItem>]
  }
  // 转化为二维数组后需要将每一项包装成ColumnItem
  const result: Array<Array<ColumnItem>> = (tempArray as Array<Array<string | number | ColumnItem>>).map((col) => {
    return col.map((row) => {
      // 非对象类型直接将值作为label和value
      if (!isObj(row)) {
        return {
          [valueKey]: row,
          [labelKey]: row
        }
      }
      /**
       * 针对已经是object的，修补成{valueKey,labelKey}
       * 如果没有labelKey，用valueKey代替
       * 如果没有valueKey，用labelKey代替
       * valueKey,labelKey都没有，直接抛错
       */
      // eslint-disable-next-line no-prototype-builtins
      if (!row.hasOwnProperty(valueKey) && !row.hasOwnProperty(labelKey)) {
        // eslint-disable-next-line prettier/prettier
      throw Error('Can\'t find valueKey and labelKey in columns')
      }
      // eslint-disable-next-line no-prototype-builtins
      if (!row.hasOwnProperty(labelKey)) {
        row[labelKey] = row[valueKey]
      }
      // eslint-disable-next-line no-prototype-builtins
      if (!row.hasOwnProperty(valueKey)) {
        row[valueKey] = row[labelKey]
      }
      return row
    })
  })

  return result
}
