/**
 * Copyright (c) 2022 - present TinyVue Authors.
 * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
 *
 * Use of this source code is governed by an MIT-style license.
 *
 * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
 * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
 *
 */

import type {
  INumericApi,
  INumericProps,
  INumericState,
  ISharedRenderlessParamHooks,
  INumericRenderlessParamUtils,
  INumericRenderlessParams,
  INumericInitStateParams
} from '@/types'
import {
  watchValue,
  toPrecision,
  getPrecision,
  internalIncrease,
  internalDecrease,
  focus,
  decrease,
  handleBlur,
  handleFocus,
  setCurrentValue,
  handleInput,
  handleInputChange,
  select,
  mounted,
  updated,
  increase,
  displayValue,
  getNumPecision,
  mouseEvent,
  getUnitPrecision,
  getDecimal,
  unmounted,
  initService,
  getDisplayOnlyText,
  filterValue,
  handleClear,
  handleChange
} from './index'

export const api = [
  'state',
  'decrease',
  'increase',
  'handleBlur',
  'handleFocus',
  'handleInput',
  'handleInputChange',
  'mouseEvent',
  'focus',
  'select',
  'handleClear',
  'handleChange'
]

const initState = ({
  reactive,
  computed,
  props,
  api,
  $service,
  constants,
  parent
}: INumericInitStateParams): INumericState => {
  const state = reactive({
    currentValue: props.modelValue,
    userInput: props.modelValue,
    lastInput: props.modelValue,
    inputStatus: false,
    decimal: null,
    strictInput: computed(() => props.strictInput),
    inputSize: computed(() => props.size || state.formSize),
    formSize: computed(() => (parent.tinyForm || {}).size),
    formDisabled: computed(() => (parent.tinyForm || {}).disabled),
    inputDisabled: computed(() => props.disabled || state.formDisabled),
    displayValue: computed(() => api.displayValue()),
    numPrecision: computed(() => api.getNumPecision()),

    minDisabled: computed(() => (!props.circulate && state.currentValue <= props.min) || state.formDisabled),

    maxDisabled: computed(() => (!props.circulate && state.currentValue >= props.max) || state.formDisabled),

    controlsAtRight: computed(() => props.controls && props.controlsPosition === 'right'),

    format: computed(() => getUnitPrecision({ service: $service, props })),

    filterMenu: constants.FILTER_OPTION,
    filterValue: computed(() => api.filterValue()),

    handleClear: computed(() => api.handleClear()),
    handleChange: computed(() => api.handleClear()),
    isDisplayOnly: computed(() => props.displayOnly || (parent.tinyForm || {}).displayOnly),
    displayOnlyText: computed(() => api.getDisplayOnlyText()),
    controls: props.controls
  })
  return state
}

const initApi = ({
  api,
  props,
  state,
  parent,
  vm,
  emit,
  dispatch,
  constants,
  nextTick
}: Pick<
  INumericRenderlessParams,
  'api' | 'props' | 'state' | 'parent' | 'vm' | 'emit' | 'dispatch' | 'constants' | 'nextTick'
>): void => {
  Object.assign(api, {
    state,
    focus: focus(vm),
    select: select(vm),
    getPrecision: getPrecision(),
    toPrecision: toPrecision(state),
    updated: updated({ constants, parent, state }),
    mounted: mounted({ constants, parent, props, state }),
    unmounted: unmounted({ parent, state }),
    getDecimal: getDecimal(props),
    handleFocus: handleFocus({ emit, state, props, api, vm }),
    decrease: decrease({ api, props, state }),
    increase: increase({ api, props, state }),
    handleInput: handleInput({ state, api, emit, props }),
    getNumPecision: getNumPecision({ api, props }),
    displayValue: displayValue({ props, state, api }),
    internalDecrease: internalDecrease({ api, state }),
    internalIncrease: internalIncrease({ api, state }),
    handleInputChange: handleInputChange({ api, state, props }),
    mouseEvent: mouseEvent({ api, props, state }),
    handleBlur: handleBlur({ constants, dispatch, emit, props, state, api }),
    watchValue: watchValue({ api, props, state }),
    setCurrentValue: setCurrentValue({ api, constants, dispatch, emit, props, state }),
    getDisplayOnlyText: getDisplayOnlyText({ parent, props, state }),
    filterValue: filterValue({ state }),
    handleClear: handleClear({ state, emit }),
    handleChange: handleChange({ state, emit })
  })

  api.getDecimal(0)
}

const initWatch = ({
  watch,
  props,
  api
}: Pick<INumericRenderlessParams, 'state' | 'watch' | 'props' | 'api'>): void => {
  watch(
    () => [props.max, props.min],
    ([curMax, curMin]) => {
      if (curMax < curMin) {
        throw new Error('[Numeric]: The maximum value should not be less than to the minimum value')
      }
    },
    { immediate: true }
  )

  watch(() => props.modelValue, api.watchValue, { immediate: true })
}

export const renderless = (
  props: INumericProps,
  { computed, onMounted, onUpdated, onUnmounted, reactive, watch, inject }: ISharedRenderlessParamHooks,
  { parent, emit, vm, constants, dispatch, service, nextTick }: INumericRenderlessParamUtils
): INumericApi => {
  const api = {} as INumericApi
  const $service = initService(service)
  const state = initState({ reactive, computed, props, api, constants, $service, parent })

  parent.tinyForm = parent.tinyForm || inject('form', null)

  initApi({ api, props, state, parent, vm, emit, dispatch, constants, nextTick })
  initWatch({ watch, props, api })

  onMounted(api.mounted)
  onUpdated(api.updated)
  onUnmounted(api.unmounted)

  return api
}