<script lang="ts" setup>
import Schema from 'async-validator';
import { computed, reactive, ref, watch } from 'vue';
import { ipProps } from '../../model/propsData';
import {
  JS_KEY_0,
  JS_KEY_0P,
  JS_KEY_9,
  JS_KEY_9P,
  JS_KEY_BACKSPACE,
  JS_KEY_DELP,
  JS_KEY_DOT,
  JS_KEY_LEFT,
  JS_KEY_RIGHT,
} from '@/pages/Service/Kvm/utils/keysCode';
import { findAimClassDom } from '@/utils/utils';

/* eslint-disable-next-line vue/valid-define-props */
const props: any = defineProps(ipProps);

const emits = defineEmits(['change', 'validate', 'focus', 'blur', 'input', 'keyup']);

let trigger = 'blur';

// 校验错误时的错误信息，空字符串的时候表示校验正确
const errorMsg = ref('');

// 校验规则
let inputRule: any = reactive({
  ip: props.rule,
});

let validator = new Schema(inputRule);

watch(
  () => props.value,
  (val: string) => {
    processingIp(val);
    if (props.rule['trigger'] === 'change') {
      // 进行校验
      validate();
    }
  },
);

watch(
  () => props.rule,
  (val: any) => {
    inputRule.ip = val;
    setTrigger();
    validator = new Schema(inputRule);
  },
);

defineExpose({
  // 清空校验
  clearValidate() {
    errorMsg.value = '';
  },
  // 进行校验
  validate() {
    validate();
  },
});

// 4个输入框的ref对象
const input1 = ref();
const input2 = ref();
const input3 = ref();
const input4 = ref();

// 上一次的数据
let preData = {
  part1: '',
  part2: '',
  part3: '',
  part4: '',
};

// 编辑中的数据
let editData = reactive({
  part1: '',
  part2: '',
  part3: '',
  part4: '',
});

let focused = ref(false);
let mouseFlag = false;
// 记录输入框focus状态
let focusState = reactive({
  part1: false,
  part2: false,
  part3: false,
  part4: false,
});

const focusRes = computed(() => {
  return focusState.part1 || focusState.part2 || focusState.part3 || focusState.part4;
});

// 4个单元输入框都blur才是真正的blur
watch(focusRes, (val: boolean) => {
  if (!val) {
    emits('blur');
  }
});

// 整合ip
function consolidationIp(): string {
  let str = '';
  if (editData.part1 || editData.part2 || editData.part3 || editData.part4) {
    str = editData.part1 + '.' + editData.part2 + '.' + editData.part3 + '.' + editData.part4;
  }

  return str;
}

// 加工ip数据
function processingIp(ip: string) {
  const arr = ip.split('.');
  editData.part1 = arr[0] || '';
  editData.part2 = arr[1] || '';
  editData.part3 = arr[2] || '';
  editData.part4 = arr[3] || '';

  preData.part1 = arr[0] || '';
  preData.part2 = arr[1] || '';
  preData.part3 = arr[2] || '';
  preData.part4 = arr[3] || '';
  consolidationIp();
}

processingIp(props.value);

// 设置校验方式
function setTrigger() {
  if (props.rule && props.rule.trigger) {
    trigger = props.rule.trigger;
  }
}

setTrigger();

// 输入框内容发生修改
function change() {
  emits('change', consolidationIp());
}

// 表单校验
function validate() {
  validator
    .validate({ ip: consolidationIp() })
    .then(() => {
      errorMsg.value = '';
      emits('validate', true);
    })
    .catch(({ errors, fields }) => {
      errorMsg.value = fields.ip[0]?.message;
      emits('validate', false);
    });
}

// 获取焦点
function focus(key: string) {
  focusState[key] = true;
  focused.value = true;
  // foucs的时候清空校验
  errorMsg.value = '';
  emits('focus');
}

// 失去焦点
function blur(key: string, num: number) {
  focusState[key] = false;
  if (trigger === 'blur') {
    blurAction(key);
  }
  // 这里设置了一个开关来控制是否需要进行关闭popover，不然从一个单元输入框中点击进入另外一个单元输入框的时候，popover不展示
  if (!mouseFlag) {
    focused.value = false;
  }
}

// 注释
function clearAndSetZero() {
  // 如果以0或者00开头，需要去除这部分的0
  let flag1 = false;
  for (let key in editData) {
    if (editData[key] && editData[key].length > 1 && editData[key].indexOf('0') === 0) {
      flag1 = true;
      editData[key] = editData[key].replace('0', '');
      if (editData[key].length > 1 && editData[key].indexOf('0') === 0) {
        editData[key] = editData[key].replace('0', '');
      }
    }
  }

  // 如果另外几个有内容，那么需要重置为0
  let flag = false;
  for (let key in editData) {
    if (editData[key]) {
      flag = true;
      break;
    }
  }
  let isEmpty = false;
  if (flag) {
    for (let key in editData) {
      if (!editData[key]) {
        editData[key] = '0';
        isEmpty = true;
      }
    }
  }

  if (flag1 || isEmpty) {
    emits('input', consolidationIp());
  }
}

function blurAction(key: string) {
  // 可能从一个单元输入框foucs到另外一个，所以这里使用定时器延迟进行计算判断
  setTimeout(() => {
    for (let s in focusState) {
      if (focusState[s]) {
        return;
      }
    }

    // 真正的blur才会执行下面的代码执行

    // 去除多余的0和补齐0
    clearAndSetZero();

    // 进行校验
    validate();
  }, 0);
}

// 输入框内容发生修改
function input(value: string, key: string, num: number) {
  // 如果输入中文或者英文
  if (!/^[0-9]*$/.test(value)) {
    editData[key] = preData[key];
    return;
  }

  if (value && value.length === 3 && Number(value) > 255) {
    editData[key] = '255';
  }

  if (trigger === 'change') {
    // 进行校验
    validate();
  }

  emits('input', consolidationIp());
}

function inputAction(key: string, containPoint?: boolean) {
  switch (key) {
    case 'part1': {
      input2.value.focus();
      setTimeout(() => {
        const selectionEnd = containPoint ? editData['part2'].length : 0;
        findAimClassDom(input2.value.$el, 'el-input__inner').setSelectionRange(0, selectionEnd);
      }, 0);
      break;
    }
    case 'part2': {
      input3.value.focus();
      setTimeout(() => {
        const selectionEnd = containPoint ? editData['part3'].length : 0;
        findAimClassDom(input3.value.$el, 'el-input__inner').setSelectionRange(0, selectionEnd);
      }, 0);
      break;
    }
    case 'part3': {
      input4.value.focus();
      setTimeout(() => {
        const selectionEnd = containPoint ? editData['part4'].length : 0;
        findAimClassDom(input4.value.$el, 'el-input__inner').setSelectionRange(0, selectionEnd);
      }, 0);
      break;
    }
    default: {
      break;
    }
  }
}

// 左键、backspace按钮事件
function leftAndBacbKeydownAction(key: string) {
  switch (key) {
    case 'part2': {
      const num = editData['part1']?.length || 0;
      findAimClassDom(input1.value.$el, 'el-input__inner').setSelectionRange(num, num);
      setTimeout(() => {
        input1.value.focus();
      }, 0);
      break;
    }
    case 'part3': {
      const num = editData['part2']?.length || 0;
      findAimClassDom(input2.value.$el, 'el-input__inner').setSelectionRange(num, num);
      setTimeout(() => {
        input2.value.focus();
      }, 0);
      break;
    }
    case 'part4': {
      const num = editData['part3']?.length || 0;
      findAimClassDom(input3.value.$el, 'el-input__inner').setSelectionRange(num, num);
      setTimeout(() => {
        input3.value.focus();
      }, 0);
      break;
    }
    default: {
      break;
    }
  }
}

// 计算是否是输入数字
function isNumberKey(keyCode: number) {
  return (
    (keyCode >= JS_KEY_0 && keyCode <= JS_KEY_9) || (keyCode >= JS_KEY_0P && keyCode <= JS_KEY_9P)
  );
}

// keyup事件
function keyup(event: any, key: string, num: number) {
  emits('keyup', event);
}

// keydown事件
function keydown(event: any, key: string, num: number) {
  const keyCode = event.keyCode;

  const selectionStart = event.target.selectionStart;
  const selectionEnd = event.target.selectionEnd;
  if (
    (keyCode === JS_KEY_BACKSPACE || keyCode === JS_KEY_LEFT) &&
    selectionStart === selectionEnd &&
    selectionEnd === 0
  ) {
    // 左键和backspace键
    leftAndBacbKeydownAction(key);
  } else if (
    keyCode === JS_KEY_RIGHT &&
    selectionStart === selectionEnd &&
    selectionEnd === editData[key].length
  ) {
    // 右键
    rightKeydownAction(key);
  } else if (keyCode === JS_KEY_DELP || keyCode === JS_KEY_DOT) {
    // 数字键盘的点、英文状态下的主键盘的点
    event.preventDefault();
    // 输入点
    inputAction(key, true);
  } else if (isNumberKey(keyCode) && selectionStart === selectionEnd && selectionEnd === 3) {
    // 输入第四个的时候让下一个输入框focus，最后输入的内容填充进下一个输入框，如果下一个有3个字符了，那么也不会影响(maxlength为3已经限制了)
    nextInputFocus(key);
  }
}

// 让下一个输入框focus
function nextInputFocus(key: string) {
  switch (key) {
    case 'part1': {
      nextInputFocusAction(key);
      break;
    }
    case 'part2': {
      nextInputFocusAction(key);
      break;
    }
    case 'part3': {
      nextInputFocusAction(key);
      break;
    }
    default: {
      break;
    }
  }
}

function nextInputFocusAction(key: string) {
  switch (key) {
    case 'part1': {
      findAimClassDom(input2.value.$el, 'el-input__inner').setSelectionRange(0, 0);
      input2.value.focus();
      if (editData['part2']?.length !== 3) {
        setTimeout(() => {
          findAimClassDom(input2.value.$el, 'el-input__inner').setSelectionRange(1, 1);
        }, 0);
      }
      break;
    }
    case 'part2': {
      findAimClassDom(input3.value.$el, 'el-input__inner').setSelectionRange(0, 0);
      input3.value.focus();
      if (editData['part3']?.length !== 3) {
        setTimeout(() => {
          findAimClassDom(input3.value.$el, 'el-input__inner').setSelectionRange(1, 1);
        }, 0);
      }
      break;
    }
    case 'part3': {
      findAimClassDom(input4.value.$el, 'el-input__inner').setSelectionRange(0, 0);
      input4.value.focus();
      if (editData['part4']?.length !== 3) {
        setTimeout(() => {
          findAimClassDom(input4.value.$el, 'el-input__inner').setSelectionRange(1, 1);
        }, 0);
      }
      break;
    }
    default: {
      break;
    }
  }
}

// 右键事件
function rightKeydownAction(key: string) {
  switch (key) {
    case 'part1': {
      findAimClassDom(input2.value.$el, 'el-input__inner').setSelectionRange(0, 0);
      setTimeout(() => {
        input2.value.focus();
      }, 0);
      break;
    }
    case 'part2': {
      findAimClassDom(input3.value.$el, 'el-input__inner').setSelectionRange(0, 0);
      setTimeout(() => {
        input3.value.focus();
      }, 0);
      break;
    }
    case 'part3': {
      findAimClassDom(input4.value.$el, 'el-input__inner').setSelectionRange(0, 0);
      setTimeout(() => {
        input4.value.focus();
      }, 0);
      break;
    }
    default: {
      break;
    }
  }
}

// 有popover输入框的鼠标事件
function mouseAction(flag: boolean) {
  mouseFlag = flag;
}
</script>

<template>
  <div class="ip-input-box">
    <el-form>
      <el-form-item
        label=""
        :class="['form-item', disabled ? '' : 'no-disabled-input-item']"
        :error="errorMsg"
      >
        <!-- 没有popover的输入框 -->
        <div
          v-if="!props.popoverSetting.show"
          :id="idStr"
          :class="['input-box', props.classStr, focused ? 'focused-input-box' : '']"
          :style="inputStyle"
        >
          <!-- 输入框之间的几个分隔点，本来是用伪类做的，但是做出来有bug：focus的时候部分被掩盖 -->
          <div class="points-box">
            <span class="point"></span>
            <span class="point"></span>
            <span class="point"></span>
          </div>

          <!-- 输入框 -->
          <el-input
            ref="input1"
            v-model="editData.part1"
            :clearable="false"
            class="input1-3"
            type="text"
            maxlength="3"
            onpaste="return false"
            :disabled="props.disabled"
            @input="input($event, 'part1', 1)"
            @change="change"
            @focus="focus('part1')"
            @blur="blur('part1', 1)"
            @keyup="keyup($event, 'part1', 1)"
            @keydown="keydown($event, 'part1', 1)"
          />

          <el-input
            ref="input2"
            v-model="editData.part2"
            :clearable="false"
            class="input1-3"
            type="text"
            maxlength="3"
            onpaste="return false"
            :disabled="props.disabled"
            @input="input($event, 'part2', 2)"
            @change="change"
            @focus="focus('part2')"
            @blur="blur('part2', 2)"
            @keyup="keyup($event, 'part2', 2)"
            @keydown="keydown($event, 'part2', 2)"
          />

          <el-input
            ref="input3"
            v-model="editData.part3"
            :clearable="false"
            class="input1-3"
            type="text"
            maxlength="3"
            onpaste="return false"
            :disabled="props.disabled"
            @input="input($event, 'part3', 3)"
            @change="change"
            @focus="focus('part3')"
            @blur="blur('part3', 3)"
            @keyup="keyup($event, 'part3', 3)"
            @keydown="keydown($event, 'part3', 3)"
          />

          <el-input
            ref="input4"
            v-model="editData.part4"
            :clearable="false"
            type="text"
            maxlength="3"
            onpaste="return false"
            :disabled="props.disabled"
            @input="input($event, 'part4', 4)"
            @change="change"
            @focus="focus('part4')"
            @blur="blur('part4', 4)"
            @keyup="keyup($event, 'part4', 4)"
            @keydown="keydown($event, 'part4', 4)"
          />
        </div>
        <!-- 有popover的输入框 -->
        <el-popover
          v-else
          :visible="focused"
          :popper-class="props.popoverSetting.popperClass"
          :width="props.popoverSetting.width"
          :placement="props.popoverSetting.placement ? props.popoverSetting.placement : 'right'"
          :teleported="false"
          trigger="focus"
          trigger-keys
        >
          <!-- 简单的字符串-popover内容 -->
          <span v-if="props.popoverSetting.type === 'ordinary' || !props.popoverSetting.type">
            {{ props.popoverSetting.content }}
          </span>
          <!-- 复杂的自定义展示组件-popover内容 -->
          <component
            :is="props.popoverSetting.popoverSlotSetting.component"
            v-if="
              props.popoverSetting.popoverSlotSetting && props.popoverSetting.type === 'complex'
            "
            :data="props.popoverSetting.popoverSlotSetting.data"
          ></component>
          <template #reference>
            <div
              :id="props.idStr"
              :class="['input-box', props.classStr, focused ? 'focused-input-box' : '']"
              :style="props.inputStyle"
              @mouseenter="mouseAction(true)"
              @mouseleave="mouseAction(false)"
            >
              <!-- 输入框之间的几个分隔点 -->
              <div class="points-box">
                <span class="point"></span>
                <span class="point"></span>
                <span class="point"></span>
              </div>

              <!-- 输入框 -->
              <el-input
                ref="input1"
                v-model="editData.part1"
                :clearable="false"
                class="input1-3"
                type="text"
                maxlength="3"
                onpaste="return false"
                :disabled="props.disabled"
                @input="input($event, 'part1', 1)"
                @change="change"
                @focus="focus('part1')"
                @blur="blur('part1', 1)"
                @keyup="keyup($event, 'part1', 1)"
                @keydown="keydown($event, 'part1', 1)"
              />

              <el-input
                ref="input2"
                v-model="editData.part2"
                :clearable="false"
                class="input1-3"
                type="text"
                maxlength="3"
                onpaste="return false"
                :disabled="props.disabled"
                @input="input($event, 'part2', 2)"
                @change="change"
                @focus="focus('part2')"
                @blur="blur('part2', 2)"
                @keyup="keyup($event, 'part2', 3)"
                @keydown="keydown($event, 'part2', 3)"
              />

              <el-input
                ref="input3"
                v-model="editData.part3"
                :clearable="false"
                class="input1-3"
                type="text"
                maxlength="3"
                onpaste="return false"
                :disabled="props.disabled"
                @input="input($event, 'part3', 3)"
                @change="change"
                @focus="focus('part3')"
                @blur="blur('part3', 3)"
                @keyup="keyup($event, 'part3', 3)"
                @keydown="keydown($event, 'part3', 3)"
              />

              <el-input
                ref="input4"
                v-model="editData.part4"
                :clearable="false"
                type="text"
                maxlength="3"
                onpaste="return false"
                :disabled="props.disabled"
                @input="input($event, 'part4', 4)"
                @change="change"
                @focus="focus('part4')"
                @blur="blur('part4', 4)"
                @keyup="keyup($event, 'part4', 4)"
                @keydown="keydown($event, 'part4', 4)"
              />
            </div>
          </template>
        </el-popover>
      </el-form-item>
    </el-form>
  </div>
</template>

<style lang="scss" scoped>
.ip-input-box {
  display: inline-block;

  :deep(.el-form) {
    .input-box {
      display: flex;
      align-items: center;
      border: 1px solid var(--o-border-color-lighter);
      border-radius: 2px;
      background-color: rgba(22, 2, 2, 0);
      box-sizing: content-box;
      .points-box {
        position: absolute;
        top: 0;
        left: 0;
        z-index: 3;
        width: 100%;
        height: 32px;
        display: flex;
        align-items: center;
        justify-content: space-evenly;
        pointer-events: none;

        .point {
          display: inline-block;
          width: 2px;
          height: 2px;
          border-radius: 50%;
          background-color: var(--o-border-color-lighter);
        }
      }

      .el-input {
        border: none;

        .el-input__wrapper {
          box-shadow: none;

          .el-input__inner {
            text-align: center;
            border: none;
            line-height: 1px !important;
            border-radius: 0;
            padding: 7px 0 !important;
            box-shadow: none;
          }
        }
      }

      .input1-3 {
        position: relative;
      }
    }

    .focused-input-box {
      border-color: var(--o-color-primary);
      background-color: var(--o-bg-color-base);

      .el-input__inner {
        color: var(--o-text-color-primary);
      }

      .point {
        background-color: rgba(0, 0, 0, 0.5) !important;
      }
    }

    .no-disabled-input-item .input-box:hover {
      border-color: var(--o-color-primary);
      background-color: var(--o-bg-color-base);

      .el-input__inner {
        color: var(--o-text-color-primary);
      }

      .point {
        background-color: rgba(0, 0, 0, 0.5) !important;
      }
    }

    .el-form-item.is-error {
      .input-box {
        border-color: var(--o-color-danger);

        .point {
          background-color: rgba(0, 0, 0, 0.5) !important;
        }

        .el-input__inner {
          padding: 7px 0px;
        }
      }
    }

    .el-form-item {
      margin-bottom: 0;
    }

    .el-form-item.is-error .el-input__wrapper {
      border: none !important;
    }
  }
}
</style>
