import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
import { ref, watch, computed } from 'vue';
import { isUndefined, isArray, isEqual } from 'lodash-es';

function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
const useNormalModel = function (props, emit) {
  let config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  const {
    prop = 'modelValue',
    deep = false,
    isEqual: isEqual$1 = false,
    defaultValue
  } = config;
  const usingProp = prop; // 实际使用中 'modelValue' 本就应该在 Key 中
  // NOTE: 不可以使用 ref<Type> 的写法，currentValue 会被直接推导成 Props[Key]
  const currentValue = ref(!isUndefined(props[usingProp]) ? props[usingProp] : defaultValue);
  const pureUpdateCurrentValue = value => {
    if (value === currentValue.value || isEqual$1 && isEqual(value, currentValue.value)) {
      return;
    }
    currentValue.value = value;
  };
  const updateCurrentValue = value => {
    pureUpdateCurrentValue(value);
    // TODO: need a more proper way instead of `as`
    emit(`update:${usingProp}`, currentValue.value);
  };
  watch(() => props[usingProp], val => {
    if (val === currentValue.value) {
      return;
    }
    currentValue.value = val;
  }, {
    deep
  });
  return [computed({
    get() {
      return currentValue.value;
    },
    set(value) {
      updateCurrentValue(value);
    }
  }), updateCurrentValue];
};

// type IncludeArray<U> = U extends unknown[] ? U : never;
// type ExtractArray<U> = U extends unknown[] ? U : never;

/**
 * TODO: 后续优化
 * 使得 useArrayModel 在不传 config 使用 modelValue，且 modelValue 的类型不为数组的情况下，有更友好的类型报错提示
 * 目前在上述情况下，modelValue 的类型被推导为 never，保证了部分场景。
 */
/* type UseArrayModelReturn<
    Props extends Record<string, any>,
    Key extends keyof Props,
> = Props[Key] extends never
    ? never
    : [WritableComputedRef<Props[Key]>, (val: ArrayOrItem<Props[Key]>) => void]; */

const useArrayModel = function (props, emit) {
  let config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  const [computedValue, updateCurrentValue] = useNormalModel(props, emit, _objectSpread(_objectSpread({}, config), {}, {
    defaultValue: []
  }));
  if (!isArray(computedValue.value)) {
    console.warn('[useArrayModel] 绑定值类型不匹配, 仅支持数组类型, value:', props[(config === null || config === void 0 ? void 0 : config.prop) || 'modelValue']);
    updateCurrentValue([]);
  }
  const updateItem = value => {
    if (isArray(value)) {
      updateCurrentValue(value);
      return;
    }

    // 兼容重复赋值为不符合预期数据类型的场景
    let val = [];
    if (!isArray(computedValue.value)) {
      console.warn('[useArrayModel] 绑定值类型不匹配, 仅支持数组类型, value:', computedValue.value);
      val = [];
    } else {
      val = [...computedValue.value];
    }
    const index = val.indexOf(value);
    if (index !== -1) {
      val.splice(index, 1);
    } else {
      val.push(value);
    }
    updateCurrentValue(val);
  };
  return [computedValue, updateItem];
};

/**
 * useArrayModel 的返回值
 * 用于 modelValue 混杂了 useNormalModel 类型的情况，用于手动指明 useArrayModel 需要的 modelValue 类型
 */

export { useArrayModel, useNormalModel };
