import { tryOnBeforeMount, tryOnUnmounted } from '@vueuse/core';
import { getDictionaries } from '@/core/dictionary/data';
import type { Dictionaries, MaybeComputedDictValueRef, MaybeDictValue } from '@/core/dictionary/index';

type Dictionary = Dictionaries[number];

interface UseDictOptions {
  multiple?: boolean;
  separator?: string | [string, string?];
  default?: MaybeDictValue;
  /**
   * 从原值中取出的键
   * @default value
   */
  rawKey?: string;
  /**
   * 从字典中取出的键
   * @default label
   */
  dictKey?: 'label' | 'value';
  /**
   * 匹配函数
   * @param {Dictionary} dict 字典项
   * @param plain 原始值
   */
  predicate?: (dict: Dictionary, plain: MaybeDictValue) => boolean;

  ignore?: boolean;
}

export function useDict(sign: string, raw: MaybeComputedDictValueRef, options?: UseDictOptions) {
  const rawKey = options?.rawKey ?? 'value';
  const dictKey = options?.dictKey ?? 'label';
  const predicate = options?.predicate ?? ((dict, plain) => dict.value === plain);
  const ignore = options?.ignore ?? false;

  const loading = ref(false);
  const value = shallowRef<MaybeDictValue>();
  const values = shallowRef<MaybeDictValue[]>([]);
  const dictionaries = shallowRef<Dictionaries>([]);

  const get = async () => {
    loading.value = true;
    dictionaries.value = await getDictionaries(sign);
    loading.value = false;
  };

  const { stop } = watchEffect(async () => {
    const plain = toValue(raw);

    const multiple = options?.multiple ?? false;
    const separator = Array.isArray(options?.separator) ? options.separator : [options?.separator ?? ','];
    const plains = multiple && typeof plain === 'string' ? plain.split(separator[0]) : Array.isArray(plain) ? plain : [plain];

    const results: MaybeDictValue[] = [];

    for (const p of plains) {
      const r = p && typeof p === 'object' ? (p as any)[rawKey] : p;
      const v = dictionaries.value.find((dict) => predicate(dict, r))?.[dictKey] ?? options?.default ?? r;
      if (v || !ignore) results.push(v);
    }

    value.value = multiple ? results.join(separator[1] ?? separator[0]) : results[0];
    values.value = results;
  });

  tryOnBeforeMount(get);
  tryOnUnmounted(stop);

  return {
    loading,
    value,
    values,
    dictionaries,
  };
}

export type UseDict = ReturnType<typeof useDict>;
