import { ref } from 'vue';
import { defineStore } from 'pinia';
import { RDMS_OBJECT_DIRECTION_DICT_CODE, RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE } from '@/constants/dict';
import { fetchGetFrontendDictCache } from '@/service/api';
import { SetupStoreId } from '@/enum';

type DictValue = string | number | null | undefined;
type DictFilterOptions = {
  onlyEnabled?: boolean;
};
type DictLabelOptions = DictFilterOptions & {
  fallback?: string;
};
type DictLabelsOptions = DictLabelOptions & {
  separator?: string;
};

function sortDictData(list: Api.Dict.DictData[]) {
  return list.slice().sort((left, right) => left.sort - right.sort || left.label.localeCompare(right.label, 'zh-CN'));
}

function normalizeFrontendDictData(
  dictType: string,
  list: Api.Dict.FrontendDictData[],
  dictIndex: number
): Api.Dict.DictData[] {
  const normalizedList = list.map((item, itemIndex) => ({
    id: -((dictIndex + 1) * 100000 + itemIndex + 1),
    label: item.label,
    value: item.value,
    dictType: item.dictType || dictType,
    sort: item.sort,
    status: item.status ?? 0,
    remark: null,
    createTime: 0
  }));

  return sortDictData(normalizedList);
}

function normalizeFrontendDictCache(cache: Api.Dict.FrontendDictCache) {
  const entries = Object.entries(cache);

  return Object.fromEntries(
    entries.map(([dictType, list], index) => [dictType, normalizeFrontendDictData(dictType, list, index)])
  );
}

function applyDictTypeAliases(dictDataMap: Record<string, Api.Dict.DictData[]>) {
  const nextDictDataMap = { ...dictDataMap };

  // 兼容后端尚未切换完成的过渡期：旧编码仍返回时，前端统一映射到新编码。
  if (!nextDictDataMap[RDMS_OBJECT_DIRECTION_DICT_CODE] && nextDictDataMap[RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE]) {
    nextDictDataMap[RDMS_OBJECT_DIRECTION_DICT_CODE] = nextDictDataMap[RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE].map(
      item => ({
        ...item,
        dictType: RDMS_OBJECT_DIRECTION_DICT_CODE
      })
    );
  }

  return nextDictDataMap;
}

function createRuntimeDictTypes(dictDataMap: Record<string, Api.Dict.DictData[]>) {
  return Object.keys(dictDataMap).map((dictType, index) => ({
    id: -(index + 1),
    name: dictType,
    type: dictType,
    status: 0 as const,
    remark: null,
    createTime: 0
  }));
}

function findDictItem(list: Api.Dict.DictData[], value?: DictValue) {
  if (value === null || value === undefined || value === '') {
    return undefined;
  }

  return list.find(item => item.value === String(value));
}

export const useDictStore = defineStore(SetupStoreId.Dict, () => {
  const loading = ref(false);
  const initialized = ref(false);
  const dictTypes = ref<Api.Dict.DictType[]>([]);
  const dictDataMap = ref<Record<string, Api.Dict.DictData[]>>({});
  const loadedAt = ref<number | null>(null);

  let initPromise: Promise<boolean> | null = null;

  function resetDictCache() {
    dictTypes.value = [];
    dictDataMap.value = {};
    loadedAt.value = null;
    initialized.value = false;
    initPromise = null;
  }

  async function initDictCache(force = false) {
    if (initialized.value && !force) {
      return true;
    }

    if (initPromise && !force) {
      return initPromise;
    }

    if (force) {
      resetDictCache();
    }

    initPromise = (async () => {
      loading.value = true;

      const result = await fetchGetFrontendDictCache();

      loading.value = false;

      if (result.error) {
        initPromise = null;
        return false;
      }

      const normalizedDictDataMap = applyDictTypeAliases(normalizeFrontendDictCache(result.data || {}));

      dictTypes.value = createRuntimeDictTypes(normalizedDictDataMap);
      dictDataMap.value = normalizedDictDataMap;
      loadedAt.value = Date.now();
      initialized.value = true;
      initPromise = null;

      return true;
    })();

    return initPromise;
  }

  function getDictData(dictType: string, onlyEnabled = false) {
    if (!dictType) {
      return [];
    }

    const list = dictDataMap.value[dictType] || [];

    if (!onlyEnabled) {
      return list;
    }

    return list.filter(item => item.status === 0);
  }

  function getDictOptions(dictType: string, onlyEnabled = true) {
    return getDictData(dictType, onlyEnabled).map(item => ({
      label: item.label,
      value: item.value
    }));
  }

  function getDictItem(dictType: string, value?: DictValue, options: DictFilterOptions = {}) {
    return findDictItem(getDictData(dictType, options.onlyEnabled), value);
  }

  function getDictLabel(dictType: string, value?: DictValue, options: DictLabelOptions = {}) {
    const { fallback = '--', onlyEnabled = false } = options;

    if (value === null || value === undefined || value === '') {
      return fallback;
    }

    const matched = getDictItem(dictType, value, { onlyEnabled });

    return matched?.label || String(value);
  }

  function getDictLabels(dictType: string, values?: Array<DictValue> | null, options: DictLabelsOptions = {}) {
    const { fallback = '--', separator = ' / ', onlyEnabled = false } = options;

    if (!values?.length) {
      return fallback;
    }

    const labels = values
      .filter(value => value !== null && value !== undefined && value !== '')
      .map(value => getDictLabel(dictType, value, { fallback: String(value), onlyEnabled }));

    return labels.length ? labels.join(separator) : fallback;
  }

  function hasDictValue(dictType: string, value?: DictValue, options: DictFilterOptions = {}) {
    return Boolean(getDictItem(dictType, value, options));
  }

  return {
    loading,
    initialized,
    dictTypes,
    dictDataMap,
    loadedAt,
    initDictCache,
    resetDictCache,
    getDictData,
    getDictOptions,
    getDictItem,
    getDictLabel,
    getDictLabels,
    hasDictValue
  };
});
