import { ref } from 'vue';
import { defineStore } from 'pinia';
import { RDMS_OBJECT_DIRECTION_DICT_CODE, RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE } from '@/constants/dict';
import { fetchGetDictDataByCode, 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'));
}

// hex 色值兜底校验：仅接受 #RRGGBB（6 位）；其他格式（含 #RGB 简写 / rgb()）一律视为无效回落到默认渲染
const HEX_COLOR_PATTERN = /^#[0-9a-f]{6}$/i;

function normalizeColorType(raw: unknown): string | null {
  if (typeof raw !== 'string') return null;
  const trimmed = raw.trim().toLowerCase();
  return HEX_COLOR_PATTERN.test(trimmed) ? trimmed : null;
}

/**
 * 解析字典项最终展示色（hex）。
 * 精确色 cssClass 优先（覆盖 colorType 落到语义色无法区分黄/橙等场景），其次 colorType；
 * 两者都不是合法 hex 时回落 null（默认渲染）。
 */
function resolveDisplayColor(colorType: unknown, cssClass: unknown): string | null {
  return normalizeColorType(cssClass) ?? normalizeColorType(colorType);
}

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,
    colorType: resolveDisplayColor(item.colorType, item.cssClass),
    remark: item.remark ?? null,
    createTime: 0
  }));

  return sortDictData(normalizedList);
}

function normalizeDictDataItem(item: Api.Dict.DictData, dictType: string): Api.Dict.DictData {
  return {
    ...item,
    value: String(item.value),
    dictType: item.dictType || dictType,
    status: item.status ?? 0,
    colorType: resolveDisplayColor(item.colorType, item.cssClass),
    remark: item.remark ?? null
  };
}

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;
  const dictDataLoadPromises = new Map<string, Promise<boolean>>();

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

  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;
  }

  async function ensureDictData(dictType: string, force = false) {
    if (!dictType) {
      return false;
    }

    if (!initialized.value) {
      await initDictCache();
    }

    if (!force && getDictData(dictType).length > 0) {
      return true;
    }

    const pending = dictDataLoadPromises.get(dictType);
    if (pending && !force) {
      return pending;
    }

    const promise = (async () => {
      const result = await fetchGetDictDataByCode(dictType);

      if (result.error || !result.data?.list?.length) {
        return false;
      }

      dictDataMap.value = {
        ...dictDataMap.value,
        [dictType]: sortDictData(result.data.list.map(item => normalizeDictDataItem(item, dictType)))
      };
      dictTypes.value = createRuntimeDictTypes(dictDataMap.value);

      return true;
    })();

    dictDataLoadPromises.set(dictType, promise);

    try {
      return await promise;
    } finally {
      if (dictDataLoadPromises.get(dictType) === promise) {
        dictDataLoadPromises.delete(dictType);
      }
    }
  }

  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,
    ensureDictData,
    resetDictCache,
    getDictData,
    getDictOptions,
    getDictItem,
    getDictLabel,
    getDictLabels,
    hasDictValue
  };
});
