<template>
  <div class="flex scoring-panel" :class="getClass('', 'sticky')">
    <toggle-dialog-render>
      <svg-icon name="question"></svg-icon>
      <span class="m-l-mini">{{ question.mainNumber }} - {{ question.subNumber }}{{ question.mainTitle }}</span>
      <template #callback>
        <div class="grid radius-base dialog-name">
          <div class="text-center">
            <p>{{ question.mainNumber }} - {{ question.subNumber }}</p>
            <p>{{ question.mainTitle }}</p>
          </div>
        </div>
      </template>
    </toggle-dialog-render>
    <div class="flex flex-1 flex-wrap score-list">
      <div
        v-for="scoreItem in scoreList"
        :key="scoreItem"
        class="score-span"
        :class="[getClass('m-b-mini'), { active: parseFloat(`${currentScore}`) === parseFloat(`${scoreItem}`) }]"
        @click="onSetScore(scoreItem)"
      >
        {{ scoreItem }}
      </div>
      <toggle-dialog-render>
        <div class="flex items-center score-result">
          <div class="flex-1 text-center">给分</div>
          <input
            ref="refInput1"
            class="flex-1 text-center score-input score-num"
            :value="currentScore"
            @focus="onInputFocus"
            @blur="onBlur"
            @keydown="onValidScore"
            @input="scoreChange"
          />
        </div>
      </toggle-dialog-render>
    </div>
    <toggle-dialog-render dialog>
      <input
        ref="refInput2"
        class="grid radius-base score-input dialog-score"
        :value="currentScore"
        @focus="onInputFocus"
        @blur="onBlur"
        @keydown="onValidScore"
        @input="scoreChange"
      />
    </toggle-dialog-render>
    <toggle-dialog-render>
      <svg-icon
        class="pointer toggle-icon"
        :class="{ visible: props.toggleModal }"
        name="toggle-panel"
        @click="onToggleClick"
      ></svg-icon>
    </toggle-dialog-render>
  </div>
</template>

<script setup lang="ts" name="ScoringPanelItem">
import { watch, computed, ref, nextTick, withDefaults, defineComponent, useSlots } from 'vue'
import SvgIcon from '@/components/common/SvgIcon.vue'
import useVModel from '@/hooks/useVModel'
import { getNumbers } from '@/utils/common'

interface QuestionInfo {
  mainNumber: number
  mainTitle: string
  subNumber: number
  totalScore: number
  intervalScore: number
}

const props = withDefaults(
  defineProps<{
    /** 弹窗模式? */
    modal?: boolean
    /** 是否可以切换显示模式 */
    toggleModal?: boolean
    /** 分值 */
    score?: number | string | undefined
    /** active 当前正在评分 */
    active: boolean
    question: QuestionInfo
  }>(),
  { modal: false, toggleModal: true, score: void 0 }
)

const emits = defineEmits(['focused', 'toggle-click', 'enter', 'update:score'])

const dialogMode = ref<boolean>(props.modal)

const currentScore = useVModel(props, 'score')

const ToggleDialogRender = defineComponent({
  name: 'DialogHideNode',
  inheritAttrs: false,
  props: {
    dialog: {
      type: Boolean,
      default: false,
    },
  },
  render() {
    const slots = useSlots()
    const defaultSlot = slots?.default?.()
    const callBackSlot = slots?.callback?.()
    return (this.dialog ? dialogMode.value : !dialogMode.value) ? defaultSlot : callBackSlot
  },
})

const getClass = (val: string, callback?: string) => {
  return dialogMode.value ? val : callback || ''
}

const question = computed(() => {
  return props.question || ({} as QuestionInfo)
})

const scoreList = computed<number[]>(() => {
  const { totalScore, intervalScore } = question.value
  return getNumbers(totalScore || 0, intervalScore || 0)
})

const focused = ref<boolean>(false)

const refInput1 = ref<HTMLInputElement>()
const refInput2 = ref<HTMLInputElement>()

watch(
  props,
  () => {
    nextTick(() => {
      if (props.active) {
        inputFocus()
      } else {
        inputBlur()
      }
    })
  },
  { immediate: true, deep: true }
)

const onSetScore = (v: number | string) => {
  onInputFocus()
  currentScore.value = v
}

const onInputFocus = () => {
  focused.value = true
  emits('focused')
}

const onBlur = () => {
  focused.value = false
}

const inputFocus = () => {
  nextTick(() => {
    refInput1?.value?.focus()
    refInput2?.value?.focus()
  })
}

const inputBlur = () => {
  nextTick(() => {
    refInput1?.value?.blur()
    refInput2?.value?.blur()
  })
}

const KEY_WHITE_LIST1 = ['ArrowLeft', 'ArrowRight', 'Backspace', 'Delete', 'Enter']
const KEY_WHITE_LIST2 = ['\\.', '[0-9]']
const KEY_VALID_REG = new RegExp(
  KEY_WHITE_LIST1.map((k) => `\\b${k}\\b`)
    .concat(KEY_WHITE_LIST2)
    .join('|')
)

const validScore = (score: number | string) => {
  return scoreList.value.some((s) => `${s}`.startsWith(`${score}`))
}

const deleteStringChart = (str: string, index: number) => {
  return str.substring(0, index) + str.substring(index + 1)
}

const joinStringChart = (str: string, index: number, chart: string) => {
  return str.substring(0, index) + chart + str.substring(index + 1)
}

const onValidScore = (e: KeyboardEvent) => {
  const target = e.target as HTMLInputElement
  const oldScore = `${currentScore.value ?? ''}`
  const start = target.selectionStart || 0

  if (!KEY_VALID_REG.test(e.key)) {
    e.preventDefault()
    return
  }

  if (['ArrowLeft', 'ArrowRight'].includes(e.key)) {
    return
  }

  if ('Enter' === e.key) {
    if (oldScore) {
      emits('enter')
    }
    e.preventDefault()
    return
  }

  if ('Backspace' === e.key) {
    if (!validScore(deleteStringChart(oldScore, start - 1))) {
      e.preventDefault()
    }
    return
  }
  if ('Delete' === e.key) {
    if (!validScore(deleteStringChart(oldScore, start))) {
      e.preventDefault()
    }
    return
  }
  if (!validScore(joinStringChart(oldScore, start, e.key))) {
    e.preventDefault()
  }
}

const scoreChange = (e: Event) => {
  const target = e.target as HTMLInputElement
  const oldScore = `${currentScore.value || ''}`
  if (validScore(target.value)) {
    onSetScore(target.value)
  } else {
    target.value = oldScore
  }
}

const onToggleClick = () => {
  emits('toggle-click')
}
</script>

<style scoped lang="scss">
.scoring-panel {
  background-color: $MainLayoutHeaderBg;
  font-size: $MediumFont;
  margin-bottom: 6px;
  &.sticky {
    // height: $MainLayoutHeaderHeight;
    align-items: center;
    padding: 10px 20px;
  }

  .dialog-name {
    width: 98px;
    place-items: center;
    background: #f5f5f5;
  }

  .score-list {
    font-size: $BaseFont;
    margin-bottom: -8px;
    margin-left: 8px;
    .score-span {
      width: 46px;
      height: 32px;
      line-height: 32px;
      text-align: center;
      border-radius: 4px;
      border: 1px solid #e5e5e5;
      color: #666666;
      margin-left: 8px;
      margin-bottom: 8px;
      cursor: pointer;

      &.active,
      &:hover {
        background-color: $color--primary;
        color: $color--white;
      }
    }

    .score-result {
      background-color: $color--primary;
      color: $color--white;
      margin-left: 8px;
      border-radius: 6px;
      padding: 2px;
      height: 32px;
      line-height: 32px;
      min-width: 84px;
      overflow: hidden;
      margin-bottom: 8px;

      .score-num {
        width: 40px;
        height: 100%;
        line-height: 28px;
        background-color: $color--white;
        color: #e02020;
        border-radius: 0 6px 6px 0;
        outline: none;
      }
    }
  }

  .dialog-score {
    width: 80px;
    place-items: center;
    text-align: center;
    background: $color--primary;
    color: $color--white;
    font-size: 32px;
    outline: none;
  }

  .score-input {
    border: none;
    outline: none;
    font-weight: bold;
  }

  .toggle-icon {
    align-self: flex-start;
    margin-top: 6px;
    margin-left: 6px;
    opacity: 0;
    &.visible {
      opacity: 1;
    }
  }
}
</style>
