import type { CSSProperties } from 'vue';
import { computed, defineComponent, onUnmounted, reactive, ref, watch } from 'vue';
import classNames from '../_util/classNames';

export interface UnitNumberProps {
  prefixCls: string;
  value: string | number;
  offset?: number;
  current?: boolean;
}

function UnitNumber({ prefixCls, value, current, offset = 0 }: UnitNumberProps) {
  let style: CSSProperties | undefined;

  if (offset) {
    style = {
      position: 'absolute',
      top: `${offset}00%`,
      left: 0,
    };
  }

  return (
    <p
      style={style}
      class={classNames(`${prefixCls}-only-unit`, {
        current,
      })}
    >
      {value}
    </p>
  );
}

function getOffset(start: number, end: number, unit: -1 | 1) {
  let index = start;
  let offset = 0;

  while ((index + 10) % 10 !== end) {
    index += unit;
    offset += unit;
  }

  return offset;
}

export default defineComponent({
  name: 'SingleNumber',
  props: {
    prefixCls: String,
    value: String,
    count: Number,
  },
  setup(props) {
    const originValue = computed(() => Number(props.value));
    const originCount = computed(() => Math.abs(props.count));
    const state = reactive({
      prevValue: originValue.value,
      prevCount: originCount.value,
    });

    // ============================= Events =============================
    const onTransitionEnd = () => {
      state.prevValue = originValue.value;
      state.prevCount = originCount.value;
    };
    const timeout = ref();
    // Fallback if transition event not support
    watch(
      originValue,
      () => {
        clearTimeout(timeout.value);
        timeout.value = setTimeout(() => {
          onTransitionEnd();
        }, 1000);
      },
      { flush: 'post' },
    );
    onUnmounted(() => {
      clearTimeout(timeout.value);
    });

    return () => {
      let unitNodes: any[];
      let offsetStyle: CSSProperties = {};
      const value = originValue.value;
      if (state.prevValue === value || Number.isNaN(value) || Number.isNaN(state.prevValue)) {
        // Nothing to change
        unitNodes = [UnitNumber({ ...props, current: true } as UnitNumberProps)];
        offsetStyle = {
          transition: 'none',
        };
      } else {
        unitNodes = [];

        // Fill basic number units
        const end = value + 10;
        const unitNumberList: number[] = [];
        for (let index = value; index <= end; index += 1) {
          unitNumberList.push(index);
        }

        // Fill with number unit nodes
        const prevIndex = unitNumberList.findIndex(n => n % 10 === state.prevValue);
        unitNodes = unitNumberList.map((n, index) => {
          const singleUnit = n % 10;
          return UnitNumber({
            ...props,
            value: singleUnit,
            offset: index - prevIndex,
            current: index === prevIndex,
          } as UnitNumberProps);
        });

        // Calculate container offset value
        const unit = state.prevCount < originCount.value ? 1 : -1;
        offsetStyle = {
          transform: `translateY(${-getOffset(state.prevValue, value, unit)}00%)`,
        };
      }
      return (
        <span
          class={`${props.prefixCls}-only`}
          style={offsetStyle}
          onTransitionend={() => onTransitionEnd()}
        >
          {unitNodes}
        </span>
      );
    };
  },
});