import { computed, defineComponent, ref, watch } from 'vue';
import { debounce } from 'lodash-es';
import {
  getEditorEmits,
  getInputProps,
  useNamespace,
} from '@ibiz-template/vue-util';
import '@ibiz-template/theme/style/components/editor/ibiz-input/ibiz-input.scss';

const fomatFloat = function (value: number, n: number) {
  const f = Math.round(value * 10 ** n) / 10 ** n;
  let s = f.toString();
  const rs = s.indexOf('.');
  if (rs < 0) {
    s += '.';
  }
  for (let i = s.length - s.indexOf('.'); i <= n; i++) {
    s += '0';
  }
  return s;
};

export const IBizInput = defineComponent({
  name: 'IBizInput',
  props: getInputProps(),
  emits: getEditorEmits(),
  setup(props, { emit }) {
    const ns = useNamespace('input');
    const c = props.controller;
    const editorModel = c.model;
    const inputRef = ref();
    let autoSize: boolean | { minRows: Number; maxRows?: Number } = {
      minRows: 2,
    };

    // 文本域默认行数，仅在 textarea 类型下有效
    const rows = ref(2);
    if (editorModel.editorType === 'TEXTAREA_10') {
      rows.value = 10;
      autoSize = {
        minRows: 10,
      };
    }

    // 类型
    const type = computed(() => {
      if (c.model.precision !== -1) {
        return 'number';
      }
      switch (editorModel.editorType) {
        case 'TEXTBOX':
          return 'text';
        case 'PASSWORD':
          return 'password';
        case 'TEXTAREA':
        case 'TEXTAREA_10':
          return 'textarea';
        default:
          return 'string';
      }
    });

    if (editorModel.editorParams) {
      if (editorModel.editorParams.autosize) {
        try {
          autoSize = JSON.parse(editorModel.editorParams.autosize);
        } catch {
          autoSize = false;
        }
      }
    }
    const currentVal = ref<string>('');

    watch(
      () => props.value,
      (newVal, oldVal) => {
        if (newVal !== oldVal) {
          if (!newVal) {
            currentVal.value = '';
          } else {
            currentVal.value = newVal;
          }
        }
      },
      { immediate: true },
    );

    let isDebounce = false;
    let awaitSearch: () => void;
    let blurCacheValue: string | undefined;
    // 防抖值变更回调
    const debounceChange = debounce(
      (e: IData) => {
        // 拦截掉blur触发后change
        if (blurCacheValue !== e.target.value) {
          emit('change', e.target.value);
        }
        blurCacheValue = undefined;
        isDebounce = false;
        if (awaitSearch) {
          awaitSearch();
        }
      },
      300,
      { leading: true },
    );

    const matchNum = (e: IData) => {
      const newValue = e.target.value;
      const regexString = `^(\\d+\\.?\\d{0,${c.model.precision}})$`;
      const match = new RegExp(regexString).exec(newValue);
      return match;
    };

    // 值变更
    const handleChange = (e: IData) => {
      isDebounce = true;
      if (c.model.precision !== -1) {
        const match = matchNum(e);
        if (match) {
          debounceChange(e);
        }
      } else {
        debounceChange(e);
      }
    };

    const handleKeyUp = (e: KeyboardEvent) => {
      if (e && e.code === 'Enter') {
        if (isDebounce) {
          awaitSearch = () => {
            inputRef.value.$el.dispatchEvent(e);
          };
        }
      }
    };

    /**
     * blur时马上抛值变更
     * @author lxm
     * @date 2023-03-06 06:36:23
     */
    const handleBlur = () => {
      blurCacheValue = inputRef.value.currentValue;
      if (c.model.precision !== -1) {
        const multiplier = 10 ** c.model.precision;
        const num =
          Math.floor(Number(blurCacheValue) * multiplier) / multiplier;
        const number = blurCacheValue ? fomatFloat(num, c.model.precision) : '';
        currentVal.value = number;
        emit('change', number);
      } else {
        emit('change', blurCacheValue);
      }
    };

    // 自动聚焦
    if (props.autoFocus) {
      watch(inputRef, newVal => {
        if (newVal) {
          let input = newVal.$el.getElementsByTagName('input')[0];
          if (!input) {
            input = newVal.$el.getElementsByTagName('textarea')[0];
          }
          input.focus();
        }
      });
    }

    const input = (e: IData) => {
      if (c.model.precision !== -1) {
        const match = matchNum(e);
        if (!match) {
          if (!e.target.value) {
            e.target.value = '';
          } else {
            const multiplier = 10 ** c.model.precision;
            const num =
              Math.floor(Number(e.target.value) * multiplier) / multiplier;
            e.target.value = num;
          }
        }
      }
    };

    return {
      ns,
      rows,
      type,
      currentVal,
      handleChange,
      handleKeyUp,
      handleBlur,
      inputRef,
      autoSize,
      input,
      c,
    };
  },
  render(h) {
    return (
      <div
        class={[
          this.ns.b(),
          this.ns.is('textarea', Object.is(this.type, 'textarea')),
          this.disabled ? this.ns.m('disabled') : '',
          this.readonly ? this.ns.m('readonly') : '',
        ]}
      >
        {[
          this.readonly && this.currentVal,
          !this.readonly &&
            h(
              'IInput',
              {
                ref: 'inputRef',
                class: this.ns.b('input'),
                props: {
                  ...this.c.customProps,
                  value: this.currentVal,
                  placeholder: this.controller.placeHolder,
                  type: this.type,
                  rows: this.rows,
                  disabled: this.disabled,
                  autosize: this.autoSize,
                },
                on: {
                  'on-change': this.handleChange,
                  'on-blur': this.handleBlur,
                },
                nativeOn: {
                  keyup: this.handleKeyUp,
                  input: this.input,
                },
              },
              [
                this.controller.model.unitName &&
                  h('i', { class: this.ns.e('unit'), slot: 'suffix' }, [
                    this.controller.model.unitName,
                  ]),
              ],
            ),
        ]}
      </div>
    );
  },
});
