<script setup lang="ts">
import { ref, computed, watch, Ref, PropType, onMounted } from 'vue'
import { copy } from 'copy-anything'
import CCWheelPicker from '../CCWheelPicker/CCWheelPicker.vue'
import { PickerValue } from '../CCWheelPicker/utils'
import { dateFormats, PickerValueForDate, isLeapYear } from './utils/DateHelper'
import { getEachSlotFormat, FormatTypes, createEachOptions } from './utils/helpers'
import { is } from 'quasar'
const props = defineProps({
  /**
   * 値
   * @example
   */
  modelValue: { type: String },
  /**
   * フォーマット
   * @example "{yyyy}-{mm}-{dd}" スロットことに{}で囲む
   */
  format: {
    type: String,
    default: '{yyyy年}-{mm月}-{dd日} {HH}{MM}',
  },
  /**
   * モード
   * @example "date"
   */
  mode: {
    type: String as PropType<'normal' | 'date'>,
    default: 'normal',
  },
  formats: {
    type: Array as PropType<FormatTypes[]>,
    default: () => [],
  },
  /**
   * イニシャルのデータをv-modelに初回反映させるかどうか
   */
  isInitialReflect: {
    type: Boolean,
    default: true,
  },

  /**
   * イニシャルのデータを指定
   */
  initialValue: {
    type: Object as PropType<{ label: string; value: string | number }[]>,
    default: () => [],
  },
})

/**
 * emitの定義
 * 共通関数
 */
const emits = defineEmits<{
  (e: 'update:modelValue', payload: String): void
  (e: 'earlyUpdate', payload: string): void
}>()

/**
 * スロットの各フォーマット（添字と値の混合）
 */
const slotsFormat = computed(() => {
  return getEachSlotFormat(props.format)
})

/**
 * それぞれのスロットの値
 */
const eachModelValue = ref<PickerValue[]>(splitModelValue(props.initialValue, slotsFormat.value))
/**
 * propsが変更されたら初期値を更新
 */
onMounted(() => {
  // isInitialReflectがfalseの場合は初回に初期値を反映しない
  if (!props.isInitialReflect) return
  eachModelValue.value = splitModelValue(props.initialValue, slotsFormat.value)
})

const dataFormats = (props.mode == 'date' ? dateFormats : props.formats) as FormatTypes[]

/**
 * eachModelValueの変更を監視
 * 変換処理後にemit
 * mode共通
 */
watch(
  eachModelValue,
  (newVal, oldVal) => {
    const returnVal = [...newVal].map((e: PickerValue | Ref<PickerValue>) => {
      return typeof e?.value == 'string' || typeof e?.value == 'number'
        ? e?.value || ''
        : e?.value?.value || ''
    })
    if (returnVal.includes('')) return
    const res = returnVal.reduce((acc, cur, index) => {
      if (!!slotsFormat.value[index].subscript) return acc
      if (typeof acc == 'number') return acc

      return acc.replaceAll(slotsFormat.value[index].format, !!cur ? String(cur) : '-')
    }, props.format)

    emits('update:modelValue', String(res).replaceAll('{', '').replaceAll('}', ''))
  },
  { deep: true }
)

function _earlyUpdate(val, index) {
  const newVal = copy([...eachModelValue.value])
  newVal[index] = val
  const returnVal = [...newVal].map((e: PickerValue | Ref<PickerValue>) => {
    return typeof e?.value == 'string' || typeof e?.value == 'number'
      ? e?.value || ''
      : e?.value?.value || ''
  })
  if (returnVal.includes('')) return
  const res = returnVal.reduce((acc, cur, index) => {
    if (!!slotsFormat.value[index].subscript) return acc
    if (typeof acc == 'number') return acc

    return acc.replaceAll(slotsFormat.value[index].format, !!cur ? String(cur) : '-')
  }, props.format)

  emits('earlyUpdate', String(res).replaceAll('{', '').replaceAll('}', ''))
}

/**
 * modelValueを各スロット用の今の値に変換して出力
 */
function splitModelValue(
  initialValue: { label: string; value: string | number }[],
  slotsFormat: {
    format: string
    subscript: boolean
  }[]
): PickerValueForDate[] | PickerValue[] {
  const res = slotsFormat.map((e) => {
    const label = initialValue.reduce((acc, cur) => {
      return acc.replaceAll(cur.label, String(cur.value))
    }, e.format)
    return {
      value: label,
      label,
    }
  })
  return res
}

/**
 * 見た目を整えるためのgridの設定
 */
const gridColumns = computed<string>(() => {
  return slotsFormat.value.reduce((grid, el, index) => {
    if (slotsFormat.value.length - 1 === index || index == 0) return grid + '1fr '

    if (!!el.subscript) {
      const width = String(el.format).length * 16
      return grid + `${width}px `
    }
    const Opt = createEachOptions(el.format, dataFormats)
      .map((e) => e.label.length)
      .reduce((e, max) => (e > max ? e : max), 1)
    const width = Opt * 24 + 20
    return grid + `${width}px `
    // }

    // return el.format.length > 3 ? grid + '1fr ' : grid + '42px '
  }, '')
})

/** modeがdateのときに今選択している年 */
const currentYear = computed<number>(() => {
  if (props.mode !== 'date') return 0
  const yearIndex = slotsFormat.value.findIndex((e) => e.format == 'yyyy')

  const year = eachModelValue.value[yearIndex]?.value || 0
  return Number(year)
})

/** modeがdateのときに今選択している月 */
const currentMonth = computed<number>(() => {
  if (props.mode !== 'date') return 0
  const monthIndex = slotsFormat.value.findIndex((e) => e.format == 'mm' || e.format == 'm')

  const year = eachModelValue.value[monthIndex]?.value || 0
  return Number(year)
})

/**
 * modeがdateのときに今選択している年と月から日(閏年の時の2月とか)を計算して出力
 */
function filterOptionDays(baseOption: PickerValue[]): PickerValue[] {
  return baseOption.filter((e) => {
    if (isLeapYear(currentYear.value) && currentMonth.value == 2) {
      if (Number(e.value) <= 29) {
        return true
      }
      return false
    }
    if (!isLeapYear(currentYear.value) && currentMonth.value == 2) {
      if (Number(e.value) <= 28) {
        return true
      }
      return false
    }
    if (
      currentMonth.value == 4 ||
      currentMonth.value == 6 ||
      currentMonth.value == 9 ||
      currentMonth.value == 11
    ) {
      if (Number(e.value) <= 30) {
        return true
      }
      return false
    }
    return true
  })
}
</script>
<template>
  <div class="_datePicker">
    <div class="_flex" v-for="(val, index) in slotsFormat" :key="`slot_${index}`">
      <CCWheelPicker
        v-if="!val.subscript"
        v-model="eachModelValue[index]"
        :options="
          props.mode === 'date' && (val.format === 'dd' || val.format === 'd')
            ? filterOptionDays(createEachOptions(val.format, dataFormats))
            : createEachOptions(val.format, dataFormats)
        "
        @earlyUpdate="_earlyUpdate($event, index)"
        :selectedBorderRadius="
          index == 0
            ? ['100px', '0', '0', '100px']
            : index == slotsFormat.length - 1
            ? ['0', '100px', '100px', '0']
            : ['0', '0', '0', '0']
        "
        :align="index == 0 ? 'right' : index == slotsFormat.length - 1 ? 'left' : 'center'"
        :single="false"
        :key="`slot_${index}_${
          props.mode === 'date' && (val.format === 'dd' || val.format === 'd')
            ? String(currentYear) + String(currentMonth)
            : '0'
        }`"
        :slotKey="`slot_${index}`"
      />
      <div
        class="_subscript"
        :style="
          index == 0
            ? 'text-align:right;border-radius: 100px 0 0 100px;'
            : index == slotsFormat.length - 1
            ? 'text-align:left;border-radius: 0 100px 100px 0;'
            : 'text-align:center;border-radius: 0 0 0 0;'
        "
        v-else
      >
        {{ val.format }}
      </div>
    </div>
  </div>
</template>
<style scoped>
._datePicker {
  display: grid;
  justify-content: center;
  -webkit-align-items: center;
  align-items: center;
  grid-template-columns: v-bind(gridColumns);
}
._flex {
  width: 100%;
  position: relative;
}

._subscript {
  position: absolute;
  top: 50%;
  width: 100%;
  transform: translate(0, calc(-50% + 8px));
  overflow: hidden;
  background-color: #f8f8fa;
  line-height: 40px;
  color: #121212;
  font-size: 14px;
  padding: 0 0;
  text-align: center;
}
</style>
