<template>
  <div class="xc-select-v3 小手指"
       :class="isFocus ? 'is-focus' : ''"
       ref="selectRef"
       @keydown.enter.stop.prevent="onKeyboardSelect"
       @mousemove="mouseOverDiv = true"
       @keydown.up.stop.prevent="onKeyboardNavigate('up')"
       @keydown.down.stop.prevent="onKeyboardNavigate('down')"
       @mouseout="mouseOverDiv = false">
    <div class="box"
         :class="props.disabled ? 'is-disabled' : '' "
         @click="boxClick">
      <input type="text"
             @focus="getfocus"
             autocomplete="new-pwd"
             @blur="blur"
             :id="props.id"
             :disabled="props.disabled"
             @mousedown.stop
             :placeholder="placeholder"
             v-model="inputData"
             ref="selectInputRef"/>
      <span>
          <el-icon
              :class="showOptions ? 'turn' : ''"
              style="transition: transform 0.4s">
            <CircleClose v-if="whetherToErase" @click="clear"/>
            <ArrowDown v-else/>
          </el-icon>
      </span>
    </div>
    <transition name="el-zoom-in-top">
      <div class="selected_item"
           ref="containerRef"
           :style="{minWidth : `${selectRef?.clientWidth}` + 'px'}"
           v-show="showOptions">
        <div v-show="props.data.length === 0" class="empty_data">
          暂无数据
        </div>
        <div :style="{ height: emptyHeight + 'px' }" v-show="props.data.length > 0"/>
        <div :style="{ transform: `translateY(${translateY})`}"
             style="width: 100%;"
             v-show="props.data.length > 0">
          <table style="width: 100%;">
            <thead>
            <tr style="line-height: 33px">
              <th v-for="item in tableHeader">
                {{ item.label }}
              </th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="(item,index) in listData"
                @click.prevent.stop="clickToSelect(item)"
                @mousemove="mouseOverLi(index)"
                @mouseout="mouseOutLi()"
                @mousedown.prevent
                :class="currentIndex === startIndex + index ? 'hover' : '' "
                :style="item.code === props.modelValue[props.code] ? selectedStyle : '' ">
              <td v-for="itemTd in tableHeader">
                {{ item[itemTd.prop] }}
              </td>
            </tr>
            </tbody>
          </table>
        </div>
      </div>
    </transition>
  </div>
</template>

<script setup name='XcSelectV3'>
import {onClickOutside} from '@vueuse/core'
import {functionDebounce} from "@/utils/debounce";
import sleep from "@/utils/sleep";
import {stringIsBlank} from "@/utils/blank-utils";

const props = defineProps({
  modelValue: {
    type: Object,
  },
  data: {
    type: Array,
    default: []
  },
  code: {
    type: String,
    default: 'code'
  },
  name: {
    type: String,
    default: 'name'
  },
  clearable: {
    type: Boolean,
    default: false
  },
  id: {
    type: String
  },
  disabled: {
    type: Boolean,
    default: false
  },
  remoteMethod: {
    type: Function,
    default: (val) => {
      console.log('搜索内容%s', val)
    }
  },
})


const emit = defineEmits(['change', 'rowClick'])

const judgingCodeIsEmpty = () => {
  if (props.modelValue[props.code] === null) {
    return true
  }
  let codeType = typeof props.modelValue[props.code]

  switch (codeType) {
    case "number":
      return props.modelValue[props.code] === null;
    case "string":
      return stringIsBlank(props.modelValue[props.code])
  }

}

const findSpecifiedData = () => {
  return new Promise((resolve, reject) => {
    if (judgingCodeIsEmpty()) {
      props.modelValue[props.name] = null
      reject();
      return;
    }
    dataLength = props.data.length
    if (dataLength === 0) {
      reject()
    }
    for (let i = 0; i < dataLength; i++) {
      if (props.data[i]['code'] === props.modelValue[props.code]) {
        resolve({item: props.data[i], index: i})
        return
      }
    }
    reject()
  })
}

const selectRef = ref(null)
let isFocus = $ref(false)

onClickOutside(selectRef, () => {
  closePopup()
})

const selectInputRef = ref(null)
const boxClick = async () => {
  if (props.disabled) {
    return
  }
  await nextTick()
  selectInputRef.value?.focus()
  isFocus = true
  showOptions = !showOptions
  if (showOptions) {
    ifVisibleArea()
    await edgeDetectionJudgment()
  }

}


let placeholder = $ref('请选择')
const setUpSearchPlaceholders = () => {
  placeholder = props.modelValue[props.name]
  inputData = null
}

//边缘检测判断
const edgeDetectionJudgment = async () => {
  await nextTick()
  let {top, left} = selectRef.value.getBoundingClientRect()
  let {clientHeight: inputHeight, clientWidth: inputWidth} = selectRef.value
  let {clientHeight, clientWidth} = containerRef.value

  const {innerWidth: windowWidth, innerHeight: windowHeight} = window;

  if (left + clientWidth > windowWidth) {
    containerRef.value.style.right = '0px'
  }

}

// 输入框焦点
const getfocus = async () => {
  await nextTick()
  selectInputRef.value?.focus()
  isFocus = true
  await sleep(100)
  setUpSearchPlaceholders()
}

const blur = () => {
  isFocus = false
  closePopup()
}

// 关闭选项窗口
const closePopup = () => {
  showOptions = false
  inputData = props.modelValue[props.name]
  currentIndex = -1
  if (judgingCodeIsEmpty()) {
    placeholder = '请选择'
  }
}


// 是否可以删除
let mouseOverDiv = $ref(false)
const whetherToErase = computed(() => {
  return props.clearable && mouseOverDiv && props.modelValue[props.code]
})

// 显示 下拉框
let showOptions = $ref(false)

// 检查数据是否在可视区域
const ifVisibleArea = () => {
  let value = listData.value.filter((item, index) => {
    if (item.code === props.modelValue[props.code]) {
      currentIndex = startIndex + index
      return true
    }
  })

  if (value.length > 0) return;

  findSpecifiedData().then(async ({item, index}) => {
    await nextTick()
    currentIndex = index
    containerRef.value.scrollTop = index * itemHeight
  }).catch(() => {
  })

}

// 选中的数据 的 label
let inputData = $ref('')

// 键盘事件
const onKeyboardSelect = () => {
  if (props.disabled) {
    return
  }
  if (currentIndex === -1) {
    boxClick()
    setUpSearchPlaceholders()
  } else {
    clickToSelect(props.data[currentIndex])
  }

}

// 上下选中事件
let currentIndex = $ref(-1)
const onKeyboardNavigate = async (position) => {

  if (!showOptions) return

  if (position === 'up') {
    currentIndex--
    if (currentIndex < 0) {
      currentIndex = dataLength - 1
    }
  } else {
    currentIndex++
    if (currentIndex === dataLength) {
      currentIndex = 0
    }
  }

  let hoverIndex = currentIndex - startIndex

  if (hoverIndex > itemCount - 1) {
    containerRef.value.scrollTop += 34
  } else if (hoverIndex < 0) {
    containerRef.value.scrollTop -= 34
  }

  if (currentIndex === 0) {
    containerRef.value.scrollTop = 0
  } else if (currentIndex === dataLength - 1) {
    containerRef.value.scrollTop = containerRef.value.scrollHeight
  }


}


// 鼠标移动到
let mousePosition = -1;
const mouseOverLi = (index) => {

  if (index !== mousePosition) {
    mousePosition = index
    currentIndex = startIndex + index
  }

}

const mouseOutLi = () => {
  mousePosition = -1
}

// 清空
const clear = () => {
  props.modelValue[props.code] = null
  props.modelValue[props.name] = null
  inputData = null
}

// 每个选项的大小
const itemHeight = 34
// 数据的长度
let dataLength = 0;
// 空的div 大小
let emptyHeight = $ref()
// 开始位置
let startIndex = $ref(0)
// 一次显示多少个
let itemCount = $ref(7)
//
let translateY = $ref(0)

const dataFilling = functionDebounce(() => {
  findSpecifiedData().then(({item, index}) => {
    clickToSelect(item)
  }).catch(() => {
    if (!showOptions) {
      if (props.modelValue[props.name]) {
        inputData = props.modelValue[props.name]
      } else {
        inputData = props.modelValue[props.code]
      }
    }
  })
}, 50)


watch(() => props.data, async () => {
  startIndex = 0
  translateY = 0
  currentIndex = -1
  dataLength = props.data.length
  emptyHeight = itemHeight * (dataLength + 2)
  if (dataLength <= itemCount) {
    emptyHeight = 0
  }
  await nextTick()
  enableScrollBar()
  containerRef.value.scrollTop = 0
  if (isFocus) return
  if (!judgingCodeIsEmpty()) {
    dataFilling()
  }
}, {deep: true, immediate: true})

watch(() => props.modelValue[props.code], async () => {
  await nextTick()
  // 寻找数据
  dataFilling()
  if (judgingCodeIsEmpty()) {
    placeholder = '请选择'
    inputData = null
  }
  emit('change')
}, {deep: true, immediate: true})


const listData = computed(() => {
  return props.data.slice(startIndex, startIndex + itemCount)
})


// 监听容器事件
const containerRef = ref(null)
const enableScrollBar = () => {
  containerRef.value.addEventListener('scroll', e => {
    const {scrollTop} = e.target
    startIndex = Math.floor(scrollTop / itemHeight)
    translateY = scrollTop + 'px'
    edgeDetectionJudgment()
  })
}

// 点击li 选中数据
const clickToSelect = (val) => {
  props.modelValue[props.code] = val.code
  props.modelValue[props.name] = val.name
  inputData = val[props.name]
  closePopup()
  emit('rowClick', val)
}

//选中样式
const selectedStyle = {
  backgroundColor: '#409eff',
  color: '#fff'
}

// 远程搜索
const performASearch = functionDebounce(() => {
  props.remoteMethod(inputData)
}, 200)

const focus = () => {
  getfocus()
}

defineExpose({focus})


let tableHeader = $ref([])

onMounted(() => {
  nextTick(() => {
    selectInputRef.value.addEventListener('input', () => {
      performASearch()
      showOptions = true
    })
  })
  if (!!useSlots().default) {
    useSlots().default().forEach((item) => {
      tableHeader.push(item.props)
    })
  } else {
    tableHeader.push({label: '编码', prop: 'code'})
    tableHeader.push({label: '名称', prop: 'name'})
  }
  performASearch()
})


</script>

<style scoped lang="scss">

.is-focus {
  box-shadow: 0 0 0 1px #409eff inset !important;
}

.empty_data {
  display: flex;
  width: 100%;
  justify-content: center;
  align-items: center;
  background-color: white;
  color: #000;
}

.xc-select-v3 {
  box-shadow: 0 0 0 1px #a8abb2 inset;
  display: inline-block;
  position: relative;
  vertical-align: middle;
  line-height: 22px;
  border-radius: 4px;
  background-color: white;

  .is-disabled {
    cursor: not-allowed;
    background-color: #f5f7fa;
    border: 1px solid #e4e7ed;
    color: #a8abb2;
  }

  .box {
    padding: 0 5px;
    display: flex;
    vertical-align: middle;

    span {
      font-size: 16px;
      align-items: center;
      display: flex;
    }

    input {
      width: 100%;
      flex-grow: 1;
      -webkit-appearance: none;
      font-size: inherit;
      height: var(--el-input-inner-height);
      line-height: var(--el-input-inner-height);
      padding: 0;
      outline: none;
      border: none;
      background: none;
      box-sizing: border-box;
    }

  }

  .selected_item {
    position: absolute;
    overflow: auto;
    display: flex;
    //direction: rtl;
    z-index: 9999;
    top: 26px;
    box-shadow: 0 0 12px rgba(0, 0, 0, .12);
    background-color: white;

    min-height: 34px;
    max-height: 275px;

    &::-webkit-scrollbar {
      width: 5px;
      height: 20px;
    }

    &::-webkit-scrollbar-corner {
      background-color: transparent;
    }

    &::-webkit-scrollbar-track {
      -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
      border-radius: 10px;
      background: #ededed;
    }

    &::-webkit-scrollbar-thumb {
      border-radius: 10px;
      -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
      background: #535353;
    }
  }
}


.turn {
  transform: rotate(-180deg)
}

table {
  border-spacing: 0;
  background-color: white;

  th {
    text-align: left;
  }
}

.hover {
  background-color: #909399;
  color: white;
}

th, td {
  border-bottom: 1px solid #000;
  white-space: nowrap;
  padding: 0 5px;
}

tr {
  line-height: 33px;
}


</style>
