export const DICTIONARY_NAMES = [
  "roleCodes",
  "allowedModules",
  "documentTypes",
  "documentStatuses",
  "securityLevels",
  "visibilityTypes",
  "attachmentTypes",
  "subjectTypes",
  "allowedActions",
  "categoryTypes",
  "enabledStatuses",
  "auditResults",
  "auditActions",
  "auditTargets",
] as const;

export type DictionaryName = (typeof DICTIONARY_NAMES)[number];

export interface DictionaryOption {
  code: string;
  label: string;
  sortNo: number;
}

export type DictionaryGroups = {
  [Name in DictionaryName]: DictionaryOption[];
};

export interface UiDictionaries {
  version: string;
  locale: string;
  dictionaries: DictionaryGroups;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return !!value && typeof value === "object" && !Array.isArray(value);
}

function hasExactKeys(
  value: Record<string, unknown>,
  expected: readonly string[],
): boolean {
  const actual = Object.keys(value);
  return (
    actual.length === expected.length &&
    actual.every((key) => expected.includes(key))
  );
}

export function isDictionaryOption(
  value: unknown,
): value is DictionaryOption {
  if (
    !isRecord(value) ||
    !hasExactKeys(value, ["code", "label", "sortNo"])
  ) {
    return false;
  }
  return (
    typeof value.code === "string" &&
    value.code.length > 0 &&
    typeof value.label === "string" &&
    value.label.trim().length > 0 &&
    typeof value.sortNo === "number" &&
    Number.isInteger(value.sortNo) &&
    value.sortNo >= 0
  );
}

function isDictionaryGroups(value: unknown): value is DictionaryGroups {
  if (!isRecord(value) || !hasExactKeys(value, DICTIONARY_NAMES)) return false;
  const valid = DICTIONARY_NAMES.every((name) => {
    const options = value[name];
    if (!Array.isArray(options) || !options.every(isDictionaryOption)) {
      return false;
    }
    const codes = options.map((option) => option.code);
    if (new Set(codes).size !== codes.length) return false;
    return true;
  });
  if (!valid) return false;
  const roleCodes = (value.roleCodes as DictionaryOption[]).map((option) => option.code).sort();
  return roleCodes.length === 2 && roleCodes[0] === "ADMIN" && roleCodes[1] === "USER";
}

export function isUiDictionaries(value: unknown): value is UiDictionaries {
  if (
    !isRecord(value) ||
    !hasExactKeys(value, ["version", "locale", "dictionaries"])
  ) {
    return false;
  }
  return (
    typeof value.version === "string" &&
    /^[0-9]{4}\.[0-9]+$/.test(value.version) &&
    value.locale === "zh-CN" &&
    isDictionaryGroups(value.dictionaries)
  );
}
