import {
   debounce,
   map,
} from 'lodash';
import {
   observable,
   toJS,
} from 'mobx';

import type {
   AdminActionItemType,
   AdminActionMenuItemType,
   AdminInitType,
   ApikeyACITType,
   ArgsAllActionsInput,
   ArgsAuthEmailInput,
   ArgsAuthEmailPrepareInput,
   ArgsAuthEmailSetInput,
   ArgsAuthPhoneInput,
   ArgsAuthPhoneSetInput,
   ArgsAuthSocInput,
   ArgsBaseInput,
   ArgsChangePasswordInput,
   ArgsCollectionAccessInput,
   ArgsCollectionChannelsInput,
   ArgsCollectionInput,
   ArgsCollectionRelationsInput,
   ArgsDataInput,
   ArgsDataSocialsInput,
   ArgsDataVizInput,
   ArgsDeleteDataInput,
   ArgsDeleteExportInput,
   ArgsDisplayValuesInput,
   ArgsEmptyDataInput,
   ArgsFilterInput,
   ArgsGeneratePaymentLinkInput,
   ArgsGeocodeInput,
   ArgsGeolocationInput,
   ArgsHallElementsInput,
   ArgsLocalizationsInput,
   ArgsMakeExportInput,
   ArgsMakeStatementExportInput,
   ArgsMoveUpdateInput,
   ArgsPrepareActionInput,
   ArgsRemoveApiKeyInput,
   ArgsRunActionInput,
   ArgsStagesTotalInput,
   ArgsUpdateInput,
   AuthEmailPrepareCFIType,
   AuthSocSTUType,
   BalancePaymentType,
   CollectionAccessType,
   CollectionCSType,
   CollectionRelationType,
   CompositeTypeGHType,
   DatumDHLTType,
   DisplayValueDLOTType,
   EmptyDatumIMType,
   FileAudioFileType,
   FileDocumentFileType,
   FileImageFileType,
   FileVideoFileType,
   GeolocationILType,
   GeopointType,
   HallElementType,
   LocalizationItemType,
   MenuABMType,
   ProfileABCSType,
   RpcResponse,
   RunActionPType,
   ShmChannelMessageType,
   ShmChannelType,
   ShmExportType,
   ShmMsgAuthType,
   ShmMsgSetType,
   ShmSocialType,
   StagesTotalMType,
   StructureType,
   UpdateDType,
   VizDataType,
} from './types';

/**
 * Параметр `extensions` в RPC-запросе (`rpcClient.call`, тело: `method`, `params`, `extensions`, …)
 * — это массив имён **связанных коллекций**, которые серверу нужно **догрузить** к основному ответу
 * метода, не включая эти сущности в `result` целиком.
 *
 * **Зачем:** в схеме (см. `BaseType::extensionsStructure`) у полей-ссылок (`ID` / `IDs`) есть
 * соответствующий документ в MongoDB. Без `extensions` в `result` остаются только идентификаторы;
 * с `extensions` сервер (`ExtensionsResolver` в schema) по этим id выбирает документы из кэша/БД
 * и возвращает их **отдельно** в поле `extensions` ответа: объект «имя коллекции → массив документов».
 * Так клиент выбирает, какой граф данных подтягивать, и не платит за лишние join’ы.
 *
 * **Синтаксис строк:** `коллекция` — подгрузить на первом уровне от корневого результата;
 * `a->b` и `a->b->c` — те же коллекции, но для **вложенных** уровней: сначала грузятся `a`, затем
 * по id из полученных документов — `b`, затем `c` (см. `oneSubLevelExtensions` / `twoSubLevelExtensions`
 * в `ExtensionsResolver`). Разрешённые значения для каждого метода задаются кодогеном из схемы.
 *
 * Перед `undefined` у конкретного вызова используется `rpcClient.setDefaultExtensions` (если задано).
 * Пустой массив — дополнительные коллекции не запрашивать.
 * 
 * Далее в коде можно получить данные по примеру ExtensionStore.citiesByID(X) или ExtensionStore.citiesByIDs([X, Y, Z]) и т.д.
 */
export const rpc = {
   allActions: (params: ArgsAllActionsInput) => {
      return rpcClient.call<ArgsAllActionsInput, AdminActionMenuItemType[] | null, null>(
         'allActions', params, []
      );
   },
   apikeys: () => {
      return rpcClient.call<{}, ApikeyACITType[] | null, null>(
         'apikeys', {}, []
      );
   },
   audioUpload: (formData: FormData) => {
      return rpcClient.callFormData<FileAudioFileType | null>(
         'audioUpload', formData
      );
   },
   authEmail: (params: ArgsAuthEmailInput) => {
      return rpcClient.call<ArgsAuthEmailInput, string | null, null>(
         'authEmail', params, []
      );
   },
   authEmailPrepare: (params: ArgsAuthEmailPrepareInput) => {
      return rpcClient.call<ArgsAuthEmailPrepareInput, AuthEmailPrepareCFIType | null, null>(
         'authEmailPrepare', params, []
      );
   },
   authEmailSet: (params: ArgsAuthEmailSetInput) => {
      return rpcClient.call<ArgsAuthEmailSetInput, boolean | null, null>(
         'authEmailSet', params, []
      );
   },
   authPhone: (params: ArgsAuthPhoneInput) => {
      return rpcClient.call<ArgsAuthPhoneInput, ShmMsgAuthType | null, null>(
         'authPhone', params, []
      );
   },
   authPhoneSet: (params: ArgsAuthPhoneSetInput) => {
      return rpcClient.call<ArgsAuthPhoneSetInput, ShmMsgSetType | null, null>(
         'authPhoneSet', params, []
      );
   },
   authSoc: (params: ArgsAuthSocInput) => {
      return rpcClient.call<ArgsAuthSocInput, AuthSocSTUType | null, null>(
         'authSoc', params, []
      );
   },
   base64: (params: ArgsBaseInput) => {
      return rpcClient.call<ArgsBaseInput, string | null, null>(
         'base64', params, []
      );
   },
   changePassword: (params: ArgsChangePasswordInput) => {
      return rpcClient.call<ArgsChangePasswordInput, boolean | null, null>(
         'changePassword', params, []
      );
   },
   collection: (params: ArgsCollectionInput) => {
      return rpcClient.call<ArgsCollectionInput, CollectionCSType | null, null>(
         'collection', params, []
      );
   },
   collectionAccess: (params: ArgsCollectionAccessInput) => {
      return rpcClient.call<ArgsCollectionAccessInput, CollectionAccessType | null, null>(
         'collectionAccess', params, []
      );
   },
   collectionChannels: (params: ArgsCollectionChannelsInput, extensions?: Array<"shmChannelMessages" | "shmChannelMessages->shmChannels" | "shmChannelMessages->shmChannelMessages" | "shmChannelMessages->shmChannels->shmChannelMessages" | "shmChannelMessages->shmChannelMessages->shmChannels" | "shmChannelMessages->shmChannelMessages->shmChannelMessages">) => {
      return rpcClient.call<ArgsCollectionChannelsInput, ShmChannelType[] | null, { shmChannelMessages?: ShmChannelMessageType[] | null, shmChannels?: ShmChannelType[] | null }>(
         'collectionChannels', params, extensions
      );
   },
   collectionRelations: (params: ArgsCollectionRelationsInput) => {
      return rpcClient.call<ArgsCollectionRelationsInput, CollectionRelationType[] | null, null>(
         'collectionRelations', params, []
      );
   },
   compositeTypes: (extensions?: Array<"hallElements" | "hallElements->hallElements" | "hallElements->hallElements->hallElements">) => {
      return rpcClient.call<{}, CompositeTypeGHType | null, { hallElements?: HallElementType[] | null }>(
         'compositeTypes', {}, extensions
      );
   },
   data: (params: ArgsDataInput) => {
      return rpcClient.call<ArgsDataInput, DatumDHLTType | null, null>(
         'data', params, []
      );
   },
   dataSocials: (params: ArgsDataSocialsInput) => {
      return rpcClient.call<ArgsDataSocialsInput, ShmSocialType[] | null, null>(
         'dataSocials', params, []
      );
   },
   dataViz: (params: ArgsDataVizInput) => {
      return rpcClient.call<ArgsDataVizInput, VizDataType | null, null>(
         'dataViz', params, []
      );
   },
   deleteData: (params: ArgsDeleteDataInput) => {
      return rpcClient.call<ArgsDeleteDataInput, boolean | null, null>(
         'deleteData', params, []
      );
   },
   deleteExport: (params: ArgsDeleteExportInput) => {
      return rpcClient.call<ArgsDeleteExportInput, boolean | null, null>(
         'deleteExport', params, []
      );
   },
   displayValues: (params: ArgsDisplayValuesInput) => {
      return rpcClient.call<ArgsDisplayValuesInput, DisplayValueDLOTType | null, null>(
         'displayValues', params, []
      );
   },
   documentUpload: (formData: FormData) => {
      return rpcClient.callFormData<FileDocumentFileType | null>(
         'documentUpload', formData
      );
   },
   emptyData: (params: ArgsEmptyDataInput) => {
      return rpcClient.call<ArgsEmptyDataInput, EmptyDatumIMType | null, null>(
         'emptyData', params, []
      );
   },
   filter: (params: ArgsFilterInput) => {
      return rpcClient.call<ArgsFilterInput, StructureType | null, null>(
         'filter', params, []
      );
   },
   generatePaymentLink: (params: ArgsGeneratePaymentLinkInput) => {
      return rpcClient.call<ArgsGeneratePaymentLinkInput, string | null, null>(
         'generatePaymentLink', params, []
      );
   },
   geocode: (params: ArgsGeocodeInput) => {
      return rpcClient.call<ArgsGeocodeInput, GeopointType[] | null, null>(
         'geocode', params, []
      );
   },
   geolocation: (params: ArgsGeolocationInput) => {
      return rpcClient.call<ArgsGeolocationInput, GeolocationILType | null, null>(
         'geolocation', params, []
      );
   },
   hallElements: (params: ArgsHallElementsInput, extensions?: Array<"hallElements" | "hallElements->hallElements" | "hallElements->hallElements->hallElements">) => {
      return rpcClient.call<ArgsHallElementsInput, HallElementType[] | null, { hallElements?: HallElementType[] | null }>(
         'hallElements', params, extensions
      );
   },
   imageUpload: (formData: FormData) => {
      return rpcClient.callFormData<FileImageFileType | null>(
         'imageUpload', formData
      );
   },
   init: () => {
      return rpcClient.call<{}, AdminInitType | null, null>(
         'init', {}, []
      );
   },
   lastBalanceOperations: () => {
      return rpcClient.call<{}, BalancePaymentType[] | null, null>(
         'lastBalanceOperations', {}, []
      );
   },
   listExport: () => {
      return rpcClient.call<{}, ShmExportType[] | null, null>(
         'listExport', {}, []
      );
   },
   localizations: (params: ArgsLocalizationsInput) => {
      return rpcClient.call<ArgsLocalizationsInput, LocalizationItemType[] | null, null>(
         'localizations', params, []
      );
   },
   makeExport: (params: ArgsMakeExportInput) => {
      return rpcClient.call<ArgsMakeExportInput, boolean | null, null>(
         'makeExport', params, []
      );
   },
   makeStatementExport: (params: ArgsMakeStatementExportInput) => {
      return rpcClient.call<ArgsMakeStatementExportInput, boolean | null, null>(
         'makeStatementExport', params, []
      );
   },
   menu: () => {
      return rpcClient.call<{}, MenuABMType | null, null>(
         'menu', {}, []
      );
   },
   moveUpdate: (params: ArgsMoveUpdateInput) => {
      return rpcClient.call<ArgsMoveUpdateInput, boolean | null, null>(
         'moveUpdate', params, []
      );
   },
   newApiKey: () => {
      return rpcClient.call<{}, string | null, null>(
         'newApiKey', {}, []
      );
   },
   prepareAction: (params: ArgsPrepareActionInput) => {
      return rpcClient.call<ArgsPrepareActionInput, AdminActionItemType | null, null>(
         'prepareAction', params, []
      );
   },
   profile: () => {
      return rpcClient.call<{}, ProfileABCSType | null, null>(
         'profile', {}, []
      );
   },
   removeApiKey: (params: ArgsRemoveApiKeyInput) => {
      return rpcClient.call<ArgsRemoveApiKeyInput, boolean | null, null>(
         'removeApiKey', params, []
      );
   },
   runAction: (params: ArgsRunActionInput) => {
      return rpcClient.call<ArgsRunActionInput, RunActionPType | null, null>(
         'runAction', params, []
      );
   },
   socials: () => {
      return rpcClient.call<{}, ShmSocialType[] | null, null>(
         'socials', {}, []
      );
   },
   stagesTotal: (params: ArgsStagesTotalInput) => {
      return rpcClient.call<ArgsStagesTotalInput, StagesTotalMType | null, null>(
         'stagesTotal', params, []
      );
   },
   update: (params: ArgsUpdateInput) => {
      return rpcClient.call<ArgsUpdateInput, UpdateDType | null, null>(
         'update', params, []
      );
   },
   videoUpload: (formData: FormData) => {
      return rpcClient.callFormData<FileVideoFileType | null>(
         'videoUpload', formData
      );
   },
};
export const compositeTypes = rpc.compositeTypes;
export const geolocation = rpc.geolocation;
export const hallElements = rpc.hallElements;
export const imageUpload = rpc.imageUpload;
export const videoUpload = rpc.videoUpload;
export const audioUpload = rpc.audioUpload;
export const documentUpload = rpc.documentUpload;
export const authEmail = rpc.authEmail;
export const authEmailPrepare = rpc.authEmailPrepare;
export const authSoc = rpc.authSoc;
export const authPhone = rpc.authPhone;
export const authEmailSet = rpc.authEmailSet;
export const authPhoneSet = rpc.authPhoneSet;
export const profile = rpc.profile;
export const changePassword = rpc.changePassword;
export const init = rpc.init;
export const menu = rpc.menu;
export const dataSocials = rpc.dataSocials;
export const socials = rpc.socials;
export const displayValues = rpc.displayValues;
export const collectionChannels = rpc.collectionChannels;
export const collection = rpc.collection;
export const collectionAccess = rpc.collectionAccess;
export const collectionRelations = rpc.collectionRelations;
export const emptyData = rpc.emptyData;
export const geocode = rpc.geocode;
export const deleteData = rpc.deleteData;
export const data = rpc.data;
export const dataViz = rpc.dataViz;
export const deleteExport = rpc.deleteExport;
export const makeExport = rpc.makeExport;
export const makeStatementExport = rpc.makeStatementExport;
export const listExport = rpc.listExport;
export const filter = rpc.filter;
export const apikeys = rpc.apikeys;
export const removeApiKey = rpc.removeApiKey;
export const newApiKey = rpc.newApiKey;
export const moveUpdate = rpc.moveUpdate;
export const base64 = rpc.base64;
export const update = rpc.update;
export const runAction = rpc.runAction;
export const allActions = rpc.allActions;
export const prepareAction = rpc.prepareAction;
export const stagesTotal = rpc.stagesTotal;
export const localizations = rpc.localizations;
export const generatePaymentLink = rpc.generatePaymentLink;
export const lastBalanceOperations = rpc.lastBalanceOperations;

const uuidv4 = (): string => {
   return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
      const r = crypto.getRandomValues(new Uint8Array(1))[0] & 15;
      const v = c === 'x' ? r : (r & 0x3) | 0x8;
      return v.toString(16);
   });
}

const xorEncrypt = (text: string, key: string) => {
   const textBytes = new TextEncoder().encode(text);
   const keyBytes = new TextEncoder().encode(key);

   const result = new Uint8Array(textBytes.length);
   for (let i = 0; i < textBytes.length; i++) {
      result[i] = textBytes[i] ^ keyBytes[i % keyBytes.length];
   }


   return btoa(String.fromCharCode(...Array.from(result)));
}

const xorDecrypt = (encodedText: string, key: string) => {
   const binaryString = atob(encodedText);
   const textBytes = new Uint8Array(binaryString.length);
   for (let i = 0; i < binaryString.length; i++) {
      textBytes[i] = binaryString.charCodeAt(i);
   }

   const keyBytes = new TextEncoder().encode(key);
   const result = new Uint8Array(textBytes.length);

   for (let i = 0; i < textBytes.length; i++) {
      result[i] = textBytes[i] ^ keyBytes[i % keyBytes.length];
   }

   return new TextDecoder().decode(result);
}


export class rpcClient {
   private static domains: string[] = [];
   private static endpoint: string = '';
   private static readonly ENDPOINT_INDEX_KEY = 'rpcClient_endpointIndex';
   private static currentDomainIndex: number = 0;
   private static initialized: boolean = false;
   private static probeCompleted: boolean = false;
   private static readonly PING_TIMEOUT = 2000;
   private static headers: Record<string, string> = {};
   private static tokenGetter: (() => string | null) | null = null;
   private static tokenGetterAsync: (() => Promise<string | null>) | null = null;
   private static unauthorizedHandler: (() => void) | null = null;
   private static networkErrorHandler: (() => void) | null = null;
   private static toastHandler: ((message: string) => void) | null = null;

   public static getRedirectURL(to: string): string {

      let path = '/redirect?to=' + encodeURIComponent(to);

      let url = this.getCurrentEndpointDomain() + path;

      return url;

   }

   private static defaultExtensions: DefaultExtensionsType = [];

   public static setDefaultExtensions(extensions: DefaultExtensionsType): void {
      this.defaultExtensions = extensions;
   }

   private static abortControllerTimeout: number = 30000;

   public static setAbortControllerTimeout(timeout: number): void {
      this.abortControllerTimeout = timeout;
   }

   private static useEncrypt: boolean = false;

   public static setUseEncrypt(useEncrypt: boolean): void {
      this.useEncrypt = useEncrypt;
   }

   public static setNetworkErrorHandler(handler: () => void): void {
      this.networkErrorHandler = handler;
   }

   public static async init(): Promise<void> {
      if (this.initialized) return;
      try {
         const savedIndex = localStorage.getItem(this.ENDPOINT_INDEX_KEY);
         if (savedIndex !== null) {
            const index = parseInt(savedIndex, 10);
            if (!isNaN(index) && index >= 0 && index < this.domains.length) {
               this.currentDomainIndex = index;
            }
         }
      } catch (error) {
         console.log('⚠️ Ошибка при загрузке индекса endpoint:', error);
      }
      this.initialized = true;
   }

   private static async saveEndpointIndex(): Promise<void> {
      try {
         localStorage.setItem(this.ENDPOINT_INDEX_KEY, this.currentDomainIndex.toString());
      } catch (error) {
         console.log('⚠️ Ошибка при сохранении индекса endpoint:', error);
      }
   }

   private static switchToNextDomain(): void {
      if (this.domains.length <= 1) return;
      this.currentDomainIndex = (this.currentDomainIndex + 1) % this.domains.length;
      this.saveEndpointIndex();
   }

   private static isConfigReady(): boolean {
      return this.domains.length > 0 && this.endpoint.length > 0;
   }

   private static async waitForConfig(): Promise<void> {
      if (!this.isConfigReady()) {
         // Ждем 1 секунду и проверяем еще раз
         await new Promise<void>((resolve) => setTimeout(resolve, 1000));

         if (!this.isConfigReady()) {
            throw new Error('Domains and endpoint are not set');
         }
      }

      await this.init();
      await this.probeDomains();
   }

   private static async pingDomain(domain: string, index: number): Promise<boolean> {
      const url = this.joinUrl(domain, 'ping');
      console.log(`[rpcClient] probe: checking [${index}] ${domain} → ${url}`);

      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), this.PING_TIMEOUT);

      try {
         const response = await fetch(url, { method: 'GET', signal: controller.signal });
         const text = await response.text();
         const ok = response.ok && text.trim() === 'pong';

         if (ok) {
            console.log(`[rpcClient] probe: [${index}] ${domain} → pong`);
         } else {
            console.log(`[rpcClient] probe: [${index}] ${domain} → fail (status=${response.status}, body="${text.trim()}")`);
         }

         return ok;
      } catch (error) {
         console.log(`[rpcClient] probe: [${index}] ${domain} → fail (${error})`);
         return false;
      } finally {
         clearTimeout(timeoutId);
      }
   }

   private static async probeDomains(): Promise<void> {
      if (this.probeCompleted) {
         return;
      }

      if (this.domains.length <= 1) {
         console.log(`[rpcClient] probe: skipped (single domain) → [0] ${this.domains[0] ?? 'none'}`);
         this.probeCompleted = true;
         return;
      }

      const startIndex = this.currentDomainIndex;
      console.log(`[rpcClient] probe: start from index ${startIndex}, total ${this.domains.length} domains`);

      let selectedIndex: number | null = null;

      for (let i = 0; i < this.domains.length; i++) {
         const idx = (startIndex + i) % this.domains.length;
         if (await this.pingDomain(this.domains[idx], idx)) {
            selectedIndex = idx;
            if (idx !== this.currentDomainIndex) {
               this.currentDomainIndex = idx;
               await this.saveEndpointIndex();
            }
            break;
         }
      }

      if (selectedIndex !== null) {
         console.log(`[rpcClient] probe: selected domain [${selectedIndex}] ${this.domains[selectedIndex]}`);
      } else {
         console.log(`[rpcClient] probe: no domain responded, keeping index [${this.currentDomainIndex}] ${this.domains[this.currentDomainIndex]}`);
      }

      this.probeCompleted = true;
   }

   private static joinUrl(domain: string, endpoint: string): string {
      const base = domain.replace(/\/+$/, '');
      const path = endpoint.replace(/^\/+/, '');
      if (!path) return base;
      return `${base}/${path}`;
   }

   private static getCurrentEndpoint(): string {
      if (!this.isConfigReady()) throw new Error('Domains and endpoint are not set');
      return this.joinUrl(this.domains[this.currentDomainIndex], this.endpoint);
   }

   public static getCurrentEndpointDomain(): string {
      return this.domains[this.currentDomainIndex];
   }

   private static isNetworkError(error: any): boolean {
      return (
         error.name === 'AbortError' ||
         error.name === 'TimeoutError' ||
         error.name === 'TypeError' ||
         error.message?.includes('Network request failed') ||
         error.message?.includes('Failed to fetch') ||
         error.message?.includes('NetworkError')
      );
   }

   public static setToken(getter: () => string | null): void {
      this.tokenGetter = getter;
   }

   public static setTokenAsync(getter: () => Promise<string | null>): void {
      this.tokenGetterAsync = getter;
   }

   public static setUnauthorizedHandler(handler: () => void): void {
      this.unauthorizedHandler = handler;
   }

   public static setToast(handler: (message: string) => void): void {
      this.toastHandler = handler;
   }

   public static setDomains(domains: string[]): void {
      this.domains = domains;
      this.currentDomainIndex = 0;
      this.initialized = false;
      this.probeCompleted = false;
   }

   public static setEndpoint(endpoint: string): void {
      this.endpoint = endpoint;
      this.initialized = false;
      this.probeCompleted = false;
   }

   public static getToken(): string | null {
      if (this.tokenGetter) return this.tokenGetter();
      return null;
   }

   public static async getTokenAsync(): Promise<string | null> {
      // Приоритет у синхронной функции
      if (this.tokenGetter) {
         return this.tokenGetter();
      }
      // Если синхронной нет, используем асинхронную
      if (this.tokenGetterAsync) {
         return await this.tokenGetterAsync();
      }
      return null;
   }

   public static setHeaders(headers: Record<string, string>): void {
      this.headers = { ...this.headers, ...headers };
   }

   private static handleError(error: any): void {
      if (error?.type === 'UNAUTHORIZED' || error?.type === 'UNAUTHENTICATED') {
         if (this.unauthorizedHandler) {
            this.unauthorizedHandler();
         }
      } else if (error?.message) {
         if (this.toastHandler) {
            this.toastHandler(error.message);
         }
      }
   }

   public static async callFormData<R>(method: string, formData: FormData): Promise<R> {
      await this.waitForConfig();
      const endpoint = this.getCurrentEndpoint();

      formData.append('method', method);

      let _token = await this.getTokenAsync();
      if (_token) {
         formData.append('token', _token);
      }

      formData.append('timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);

      const context = this.useEncrypt ? uuidv4() : undefined;

      if (this.useEncrypt && context) {
         formData.append('context', context);
      }


      const headers = { ...this.headers };
      delete headers['Content-Type'];

      const controller = new AbortController();
      const timeoutId = setTimeout(() => {
         controller.abort();
      }, this.abortControllerTimeout);

      return fetch(endpoint, { method: 'POST', headers, body: formData, signal: controller.signal })
         .then(res => res.json())
         .then((json: RpcResponse<R>) => {
            clearTimeout(timeoutId);

            if (this.useEncrypt && context) {

               if (json.result) {
                  json.result = JSON.parse(xorDecrypt(json.result as string, context));
               }
               if (json.extensions) {
                  json.extensions = JSON.parse(xorDecrypt(json.extensions as string, context));
               }
            }


            if (json?.error) {
               this.handleError(json.error);
               throw json.error;
            }
            this.saveEndpointIndex();
            return json.result as R;
         })
         .catch(err => {
            clearTimeout(timeoutId);
            if (this.isNetworkError(err)) {

               if (this.networkErrorHandler) {
                  this.networkErrorHandler();
               }

               this.switchToNextDomain();
            }
            throw err;
         });
   }

   public static async call<P, R, E>(method: string, params?: P, extensions?: Array<string>): Promise<RpcResponse<R, E>> {
      await this.waitForConfig();
      const endpoint = this.getCurrentEndpoint();


      const context = this.useEncrypt ? uuidv4() : undefined;


      let _extensions = extensions != undefined ? extensions : this.defaultExtensions;


      const token = await this.getTokenAsync();
      const body = {
         method, extensions: _extensions, context: context, token: token,
         params: params ? context ? xorEncrypt(JSON.stringify(params ?? {}), context) : params : undefined,
         id: Date.now().toString()
      };

      console.log('🚀🚀🚀 method: ', method, ' RPC Endpoint: ', endpoint);
      console.log('🚀🚀🚀 method: ', method, ' RPC Body: ', JSON.stringify(body));


      const controller = new AbortController();
      const timeoutId = setTimeout(() => {
         controller.abort();
      }, this.abortControllerTimeout);

      return fetch(endpoint, {
         method: 'POST', headers: {
            'Content-Type': 'application/json',
            'timezone': Intl.DateTimeFormat().resolvedOptions().timeZone,
            ...this.headers
         }, body: JSON.stringify(body),
         signal: controller.signal
      })
         .then(res => res.json())
         .then((json: RpcResponse<R>) => {
            clearTimeout(timeoutId);

            if (this.useEncrypt && context) {

               if (json.result) {
                  json.result = JSON.parse(xorDecrypt(json.result as string, context));
               }
               if (json.extensions) {
                  json.extensions = JSON.parse(xorDecrypt(json.extensions as string, context));
               }
            }

            console.log('🍪🍪🍪 method: ', method, ' RPC Response: ', JSON.stringify(json));
            if (json?.error) {
               this.handleError(json.error);
               throw json.error;
            }
            this.saveEndpointIndex();

            if (json?.extensions) {
               ExtensionsStore.addToStore(json.extensions);
            }

            return json as RpcResponse<R, E>;
         })
         .catch(err => {
            clearTimeout(timeoutId);
            if (this.isNetworkError(err)) {
               if (this.networkErrorHandler) {
                  this.networkErrorHandler();
               }
               this.switchToNextDomain();
            }
            console.log('🛑🛑🛑 RPC Error: ', JSON.stringify(body));
            throw err;
         });
   }
}




type DefaultExtensionsType = ("hallElements" | "shmChannelMessages" | "shmChannels")[];

type ExtensionsStoreType = { hallElements: { [key: string]: HallElementType }, shmChannelMessages: { [key: string]: ShmChannelMessageType }, shmChannels: { [key: string]: ShmChannelType } };

type ExtensionsStoreInputType = { hallElements?: HallElementType[], shmChannelMessages?: ShmChannelMessageType[], shmChannels?: ShmChannelType[] };

export class ExtensionsStore {

   private static storage: { save: (key: string, value: string) => Promise<void>; get: (key: string) => Promise<string | null> } | null = null;


   /* 
   
   //Убрал так как объем данных может быть большим и не хватает памяти,
   //Нужно пересмотреть способ кеширования
   public static setStorage(storage: { save: (key: string, value: string) => Promise<void>; get: (key: string) => Promise<string | null> }): void {
       this.storage = storage;
    }*/

   public static async loadFromStorage(): Promise<void> {
      if (this.storage) {
         const extensionsStore = await this.storage.get("extensionsStore");
         if (extensionsStore) {
            try {
               let _extensionsStore: ExtensionsStoreType = JSON.parse(extensionsStore);

               map(_extensionsStore, (value: any, key: keyof ExtensionsStoreType) => {
                  if (value) {

                     if (this.store?.[key]) {
                        //@ts-ignore
                        this.store[key] = {
                           //@ts-ignore
                           ...this.store[key],
                           ...value
                        };

                     } else {
                        //@ts-ignore
                        this.store[key] = value;
                     }

                  }
               });

            } catch (error) {
               console.error("⚠️ Ошибка при загрузке extensionsStore из storage:", error);
            }

         }
      }
   }

   private static saveToStorage = debounce(() => {

      if (this.storage) {


         this.storage.save("extensionsStore", JSON.stringify(toJS(this.store)));


      }


   }, 1000);


   private static store: ExtensionsStoreType = observable({ hallElements: {}, shmChannelMessages: {}, shmChannels: {} });



   public static addToStore(value: ExtensionsStoreInputType) {

      if (value?.hallElements && value?.hallElements.length > 0) {

         for (const item of value?.hallElements) {
            if (item?._id) {
               this.store.hallElements[item._id] = item;
            }
         }

      }
      if (value?.shmChannelMessages && value?.shmChannelMessages.length > 0) {

         for (const item of value?.shmChannelMessages) {
            if (item?._id) {
               this.store.shmChannelMessages[item._id] = item;
            }
         }

      }
      if (value?.shmChannels && value?.shmChannels.length > 0) {

         for (const item of value?.shmChannels) {
            if (item?._id) {
               this.store.shmChannels[item._id] = item;
            }
         }

      }


      this.saveToStorage();
   }

   public static hallElements(): HallElementType[] {
      return Object.values(this.store.hallElements);
   }
   public static shmChannelMessages(): ShmChannelMessageType[] {
      return Object.values(this.store.shmChannelMessages);
   }
   public static shmChannels(): ShmChannelType[] {
      return Object.values(this.store.shmChannels);
   }

   public static hallElementsByID(id?: string | null): HallElementType | null {


      if (!id) {
         return null;
      }

      if (this.store.hallElements?.[id]) {
         return this.store.hallElements[id];
      } else {
         console.info("📕 Extension hallElements not found by ID: " + id);
      }

      return null;
   }
   public static shmChannelMessagesByID(id?: string | null): ShmChannelMessageType | null {


      if (!id) {
         return null;
      }

      if (this.store.shmChannelMessages?.[id]) {
         return this.store.shmChannelMessages[id];
      } else {
         console.info("📕 Extension shmChannelMessages not found by ID: " + id);
      }

      return null;
   }
   public static shmChannelsByID(id?: string | null): ShmChannelType | null {


      if (!id) {
         return null;
      }

      if (this.store.shmChannels?.[id]) {
         return this.store.shmChannels[id];
      } else {
         console.info("📕 Extension shmChannels not found by ID: " + id);
      }

      return null;
   }


   public static hallElementsByIDs(_ids: (string | undefined | null)[] | null | undefined): HallElementType[] {

      if (!_ids) {
         return [];
      }

      let ids = _ids.filter(id => id !== undefined && id !== null) as string[];

      const result = [];
      for (const id of ids) {
         if (this.store.hallElements[id]) {
            result.push(this.store.hallElements[id]);
         }
      }

      if (result.length === 0) {
         console.info("📕 Extensions hallElements not found by IDs: " + ids.join(", "));
      }

      return result;

   }
   public static shmChannelMessagesByIDs(_ids: (string | undefined | null)[] | null | undefined): ShmChannelMessageType[] {

      if (!_ids) {
         return [];
      }

      let ids = _ids.filter(id => id !== undefined && id !== null) as string[];

      const result = [];
      for (const id of ids) {
         if (this.store.shmChannelMessages[id]) {
            result.push(this.store.shmChannelMessages[id]);
         }
      }

      if (result.length === 0) {
         console.info("📕 Extensions shmChannelMessages not found by IDs: " + ids.join(", "));
      }

      return result;

   }
   public static shmChannelsByIDs(_ids: (string | undefined | null)[] | null | undefined): ShmChannelType[] {

      if (!_ids) {
         return [];
      }

      let ids = _ids.filter(id => id !== undefined && id !== null) as string[];

      const result = [];
      for (const id of ids) {
         if (this.store.shmChannels[id]) {
            result.push(this.store.shmChannels[id]);
         }
      }

      if (result.length === 0) {
         console.info("📕 Extensions shmChannels not found by IDs: " + ids.join(", "));
      }

      return result;

   }

}
export const enumHelper = {
   "hallElements": {
      "type": {
         "seat": "Место",
         "sector": "Сектор",
         "table": "Стол",
         "stage": "Сцена",
         "wall": "Стена",
         "zone": "Зона"
      }
   },
   "global": {
      "links_action": {
         "sendMessage": "sendMessage",
         "makeCall": "makeCall"
      },
      "otherLinks_action": {
         "sendMessage": "sendMessage",
         "makeCall": "makeCall"
      },
      "structure_actionPosition": {
         "sidebar": "sidebar",
         "inline": "inline",
         "table": "table"
      },
      "structure_dashboardBlockType": {
         "card": "card",
         "lineChart": "lineChart",
         "pieChart": "pieChart",
         "barChart": "barChart"
      },
      "structure_type": {
         "static": "static",
         "fileAudio": "fileAudio",
         "fileDocumentLink": "fileDocumentLink",
         "fileDocument": "fileDocument",
         "fileImageID": "fileImageID",
         "fileVideoID": "fileVideoID",
         "fileAudioID": "fileAudioID",
         "fileAudioLink": "fileAudioLink",
         "fileDocumentID": "fileDocumentID",
         "fileImageLink": "fileImageLink",
         "fileImage": "fileImage",
         "fileVideo": "fileVideo",
         "enum": "enum",
         "enums": "enums",
         "string": "string",
         "structure": "structure",
         "color": "color",
         "unixdatetime": "unixdatetime",
         "mixed": "mixed",
         "bool": "bool",
         "float": "float",
         "int": "int",
         "markdown": "markdown",
         "viz": "viz",
         "selfRef": "selfRef",
         "uuid": "uuid",
         "url": "url",
         "text": "text",
         "html": "html",
         "time": "time",
         "range": "range",
         "IDs": "IDs",
         "geopoint": "geopoint",
         "array": "array",
         "phone": "phone",
         "ID": "ID",
         "code": "code",
         "unixdate": "unixdate",
         "mongoPoint": "mongoPoint",
         "adminGroup": "adminGroup",
         "dashboard": "dashboard",
         "admin": "admin",
         "login": "login",
         "password": "password",
         "email": "email",
         "mongoPolygon": "mongoPolygon",
         "rate": "rate",
         "gradient": "gradient",
         "report": "report",
         "geoRegion": "geoRegion",
         "balance": "balance",
         "json": "json",
         "hall": "hall",
         "hallSelector": "hallSelector"
      },
      "alerts_type": {
         "error": {
            "hide": false,
            "unique": false,
            "globalUnique": false,
            "description": null,
            "path": [],
            "lookupKey": null,
            "indexed": false,
            "compositeType": false,
            "deprecated": null,
            "outputTransformers": [],
            "display": false,
            "displayPrefix": null,
            "notNull": false,
            "single": false,
            "assets": [],
            "group": {
               "key": "default"
            },
            "itemType": null,
            "key": null,
            "min": null,
            "max": null,
            "editable": false,
            "inAdmin": false,
            "inTable": false,
            "col": 24,
            "row": 1,
            "required": false,
            "nullable": true,
            "default": null,
            "title": null,
            "type": "string",
            "report": false,
            "tablePriority": null,
            "cond": null,
            "localCond": null,
            "defaultIsSet": false,
            "private": false,
            "columnsWidth": null,
            "trim": false,
            "uppercase": false,
            "lowercase": false
         },
         "warning": {
            "hide": false,
            "unique": false,
            "globalUnique": false,
            "description": null,
            "path": [],
            "lookupKey": null,
            "indexed": false,
            "compositeType": false,
            "deprecated": null,
            "outputTransformers": [],
            "display": false,
            "displayPrefix": null,
            "notNull": false,
            "single": false,
            "assets": [],
            "group": {
               "key": "default"
            },
            "itemType": null,
            "key": null,
            "min": null,
            "max": null,
            "editable": false,
            "inAdmin": false,
            "inTable": false,
            "col": 24,
            "row": 1,
            "required": false,
            "nullable": true,
            "default": null,
            "title": null,
            "type": "string",
            "report": false,
            "tablePriority": null,
            "cond": null,
            "localCond": null,
            "defaultIsSet": false,
            "private": false,
            "columnsWidth": null,
            "trim": false,
            "uppercase": false,
            "lowercase": false
         },
         "info": {
            "hide": false,
            "unique": false,
            "globalUnique": false,
            "description": null,
            "path": [],
            "lookupKey": null,
            "indexed": false,
            "compositeType": false,
            "deprecated": null,
            "outputTransformers": [],
            "display": false,
            "displayPrefix": null,
            "notNull": false,
            "single": false,
            "assets": [],
            "group": {
               "key": "default"
            },
            "itemType": null,
            "key": null,
            "min": null,
            "max": null,
            "editable": false,
            "inAdmin": false,
            "inTable": false,
            "col": 24,
            "row": 1,
            "required": false,
            "nullable": true,
            "default": null,
            "title": null,
            "type": "string",
            "report": false,
            "tablePriority": null,
            "cond": null,
            "localCond": null,
            "defaultIsSet": false,
            "private": false,
            "columnsWidth": null,
            "trim": false,
            "uppercase": false,
            "lowercase": false
         },
         "success": {
            "hide": false,
            "unique": false,
            "globalUnique": false,
            "description": null,
            "path": [],
            "lookupKey": null,
            "indexed": false,
            "compositeType": false,
            "deprecated": null,
            "outputTransformers": [],
            "display": false,
            "displayPrefix": null,
            "notNull": false,
            "single": false,
            "assets": [],
            "group": {
               "key": "default"
            },
            "itemType": null,
            "key": null,
            "min": null,
            "max": null,
            "editable": false,
            "inAdmin": false,
            "inTable": false,
            "col": 24,
            "row": 1,
            "required": false,
            "nullable": true,
            "default": null,
            "title": null,
            "type": "string",
            "report": false,
            "tablePriority": null,
            "cond": null,
            "localCond": null,
            "defaultIsSet": false,
            "private": false,
            "columnsWidth": null,
            "trim": false,
            "uppercase": false,
            "lowercase": false
         }
      },
      "alerts_navigate": {
         "tCompany": "tCompany",
         "tCompanyBank": "tCompanyBank",
         "profile": "profile",
         "openLink": "openLink"
      },
      "field_actionPosition": {
         "sidebar": "sidebar",
         "inline": "inline",
         "table": "table"
      },
      "field_dashboardBlockType": {
         "card": "card",
         "lineChart": "lineChart",
         "pieChart": "pieChart",
         "barChart": "barChart"
      },
      "field_type": {
         "static": "static",
         "fileAudio": "fileAudio",
         "fileDocumentLink": "fileDocumentLink",
         "fileDocument": "fileDocument",
         "fileImageID": "fileImageID",
         "fileVideoID": "fileVideoID",
         "fileAudioID": "fileAudioID",
         "fileAudioLink": "fileAudioLink",
         "fileDocumentID": "fileDocumentID",
         "fileImageLink": "fileImageLink",
         "fileImage": "fileImage",
         "fileVideo": "fileVideo",
         "enum": "enum",
         "enums": "enums",
         "string": "string",
         "structure": "structure",
         "color": "color",
         "unixdatetime": "unixdatetime",
         "mixed": "mixed",
         "bool": "bool",
         "float": "float",
         "int": "int",
         "markdown": "markdown",
         "viz": "viz",
         "selfRef": "selfRef",
         "uuid": "uuid",
         "url": "url",
         "text": "text",
         "html": "html",
         "time": "time",
         "range": "range",
         "IDs": "IDs",
         "geopoint": "geopoint",
         "array": "array",
         "phone": "phone",
         "ID": "ID",
         "code": "code",
         "unixdate": "unixdate",
         "mongoPoint": "mongoPoint",
         "adminGroup": "adminGroup",
         "dashboard": "dashboard",
         "admin": "admin",
         "login": "login",
         "password": "password",
         "email": "email",
         "mongoPolygon": "mongoPolygon",
         "rate": "rate",
         "gradient": "gradient",
         "report": "report",
         "geoRegion": "geoRegion",
         "balance": "balance",
         "json": "json",
         "hall": "hall",
         "hallSelector": "hallSelector"
      },
      "langs_langs": {
         "ru": "Русский",
         "en": "Английский",
         "de": "Немецкий",
         "fr": "Французский",
         "it": "Итальянский",
         "es": "Испанский",
         "pt": "Португальский",
         "hi": "Хинди",
         "bn": "Бенгальский",
         "id": "Индонезийский",
         "zh": "Китайский",
         "ja": "Японский"
      },
      "mainStructure_actionPosition": {
         "sidebar": "sidebar",
         "inline": "inline",
         "table": "table"
      },
      "mainStructure_dashboardBlockType": {
         "card": "card",
         "lineChart": "lineChart",
         "pieChart": "pieChart",
         "barChart": "barChart"
      },
      "mainStructure_type": {
         "static": "static",
         "fileAudio": "fileAudio",
         "fileDocumentLink": "fileDocumentLink",
         "fileDocument": "fileDocument",
         "fileImageID": "fileImageID",
         "fileVideoID": "fileVideoID",
         "fileAudioID": "fileAudioID",
         "fileAudioLink": "fileAudioLink",
         "fileDocumentID": "fileDocumentID",
         "fileImageLink": "fileImageLink",
         "fileImage": "fileImage",
         "fileVideo": "fileVideo",
         "enum": "enum",
         "enums": "enums",
         "string": "string",
         "structure": "structure",
         "color": "color",
         "unixdatetime": "unixdatetime",
         "mixed": "mixed",
         "bool": "bool",
         "float": "float",
         "int": "int",
         "markdown": "markdown",
         "viz": "viz",
         "selfRef": "selfRef",
         "uuid": "uuid",
         "url": "url",
         "text": "text",
         "html": "html",
         "time": "time",
         "range": "range",
         "IDs": "IDs",
         "geopoint": "geopoint",
         "array": "array",
         "phone": "phone",
         "ID": "ID",
         "code": "code",
         "unixdate": "unixdate",
         "mongoPoint": "mongoPoint",
         "adminGroup": "adminGroup",
         "dashboard": "dashboard",
         "admin": "admin",
         "login": "login",
         "password": "password",
         "email": "email",
         "mongoPolygon": "mongoPolygon",
         "rate": "rate",
         "gradient": "gradient",
         "report": "report",
         "geoRegion": "geoRegion",
         "balance": "balance",
         "json": "json",
         "hall": "hall",
         "hallSelector": "hallSelector"
      },
      "subStructure_actionPosition": {
         "sidebar": "sidebar",
         "inline": "inline",
         "table": "table"
      },
      "subStructure_dashboardBlockType": {
         "card": "card",
         "lineChart": "lineChart",
         "pieChart": "pieChart",
         "barChart": "barChart"
      },
      "subStructure_type": {
         "static": "static",
         "fileAudio": "fileAudio",
         "fileDocumentLink": "fileDocumentLink",
         "fileDocument": "fileDocument",
         "fileImageID": "fileImageID",
         "fileVideoID": "fileVideoID",
         "fileAudioID": "fileAudioID",
         "fileAudioLink": "fileAudioLink",
         "fileDocumentID": "fileDocumentID",
         "fileImageLink": "fileImageLink",
         "fileImage": "fileImage",
         "fileVideo": "fileVideo",
         "enum": "enum",
         "enums": "enums",
         "string": "string",
         "structure": "structure",
         "color": "color",
         "unixdatetime": "unixdatetime",
         "mixed": "mixed",
         "bool": "bool",
         "float": "float",
         "int": "int",
         "markdown": "markdown",
         "viz": "viz",
         "selfRef": "selfRef",
         "uuid": "uuid",
         "url": "url",
         "text": "text",
         "html": "html",
         "time": "time",
         "range": "range",
         "IDs": "IDs",
         "geopoint": "geopoint",
         "array": "array",
         "phone": "phone",
         "ID": "ID",
         "code": "code",
         "unixdate": "unixdate",
         "mongoPoint": "mongoPoint",
         "adminGroup": "adminGroup",
         "dashboard": "dashboard",
         "admin": "admin",
         "login": "login",
         "password": "password",
         "email": "email",
         "mongoPolygon": "mongoPolygon",
         "rate": "rate",
         "gradient": "gradient",
         "report": "report",
         "geoRegion": "geoRegion",
         "balance": "balance",
         "json": "json",
         "hall": "hall",
         "hallSelector": "hallSelector"
      },
      "sort_direction": {
         "ASC": "По возрастанию",
         "DESC": "По убыванию"
      },
      "filter_actionPosition": {
         "sidebar": "sidebar",
         "inline": "inline",
         "table": "table"
      },
      "filter_dashboardBlockType": {
         "card": "card",
         "lineChart": "lineChart",
         "pieChart": "pieChart",
         "barChart": "barChart"
      },
      "filter_type": {
         "static": "static",
         "fileAudio": "fileAudio",
         "fileDocumentLink": "fileDocumentLink",
         "fileDocument": "fileDocument",
         "fileImageID": "fileImageID",
         "fileVideoID": "fileVideoID",
         "fileAudioID": "fileAudioID",
         "fileAudioLink": "fileAudioLink",
         "fileDocumentID": "fileDocumentID",
         "fileImageLink": "fileImageLink",
         "fileImage": "fileImage",
         "fileVideo": "fileVideo",
         "enum": "enum",
         "enums": "enums",
         "string": "string",
         "structure": "structure",
         "color": "color",
         "unixdatetime": "unixdatetime",
         "mixed": "mixed",
         "bool": "bool",
         "float": "float",
         "int": "int",
         "markdown": "markdown",
         "viz": "viz",
         "selfRef": "selfRef",
         "uuid": "uuid",
         "url": "url",
         "text": "text",
         "html": "html",
         "time": "time",
         "range": "range",
         "IDs": "IDs",
         "geopoint": "geopoint",
         "array": "array",
         "phone": "phone",
         "ID": "ID",
         "code": "code",
         "unixdate": "unixdate",
         "mongoPoint": "mongoPoint",
         "adminGroup": "adminGroup",
         "dashboard": "dashboard",
         "admin": "admin",
         "login": "login",
         "password": "password",
         "email": "email",
         "mongoPolygon": "mongoPolygon",
         "rate": "rate",
         "gradient": "gradient",
         "report": "report",
         "geoRegion": "geoRegion",
         "balance": "balance",
         "json": "json",
         "hall": "hall",
         "hallSelector": "hallSelector"
      },
      "args_actionPosition": {
         "sidebar": "sidebar",
         "inline": "inline",
         "table": "table"
      },
      "args_dashboardBlockType": {
         "card": "card",
         "lineChart": "lineChart",
         "pieChart": "pieChart",
         "barChart": "barChart"
      },
      "args_type": {
         "static": "static",
         "fileAudio": "fileAudio",
         "fileDocumentLink": "fileDocumentLink",
         "fileDocument": "fileDocument",
         "fileImageID": "fileImageID",
         "fileVideoID": "fileVideoID",
         "fileAudioID": "fileAudioID",
         "fileAudioLink": "fileAudioLink",
         "fileDocumentID": "fileDocumentID",
         "fileImageLink": "fileImageLink",
         "fileImage": "fileImage",
         "fileVideo": "fileVideo",
         "enum": "enum",
         "enums": "enums",
         "string": "string",
         "structure": "structure",
         "color": "color",
         "unixdatetime": "unixdatetime",
         "mixed": "mixed",
         "bool": "bool",
         "float": "float",
         "int": "int",
         "markdown": "markdown",
         "viz": "viz",
         "selfRef": "selfRef",
         "uuid": "uuid",
         "url": "url",
         "text": "text",
         "html": "html",
         "time": "time",
         "range": "range",
         "IDs": "IDs",
         "geopoint": "geopoint",
         "array": "array",
         "phone": "phone",
         "ID": "ID",
         "code": "code",
         "unixdate": "unixdate",
         "mongoPoint": "mongoPoint",
         "adminGroup": "adminGroup",
         "dashboard": "dashboard",
         "admin": "admin",
         "login": "login",
         "password": "password",
         "email": "email",
         "mongoPolygon": "mongoPolygon",
         "rate": "rate",
         "gradient": "gradient",
         "report": "report",
         "geoRegion": "geoRegion",
         "balance": "balance",
         "json": "json",
         "hall": "hall",
         "hallSelector": "hallSelector"
      },
      "localizations_targetLang": {
         "ru": "Русский",
         "en": "Английский",
         "de": "Немецкий",
         "fr": "Французский",
         "it": "Итальянский",
         "es": "Испанский",
         "pt": "Португальский",
         "hi": "Хинди",
         "bn": "Бенгальский",
         "id": "Индонезийский",
         "zh": "Китайский",
         "ja": "Японский"
      }
   },
   "shmChannelMessages": {
      "type": {
         "heart": "Сердце",
         "like": "Нравится",
         "laugh": "Смех",
         "wow": "Удивление",
         "sad": "Грусть",
         "angry": "Злость",
         "clap": "Аплодисменты",
         "party": "Праздник",
         "poop": "Какахa",
         "fire": "Огонь"
      },
      "style": {
         "default": "По умолчанию",
         "primary": "Основная",
         "danger": "Опасная"
      }
   },
   "shmChannels": {
      "role": {
         "member": "Участник",
         "owner": "Владелец",
         "admin": "Администратор",
         "support": "Поддержка"
      },
      "type": {
         "default": "Обычный чат",
         "support": "Поддержка",
         "supportSystem": "Поддержка системы"
      }
   },
   "_shm_exports": {
      "type": {
         "statement": "Ведомость расчетов",
         "data": "Экспорт данные"
      },
      "status": {
         "pending": "В очереди",
         "processing": "В обработке",
         "done": "Выполнено",
         "error": "Ошибка"
      }
   }
}
export const enumHelperGet = {
   "hallElements": {
      "type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.hallElements["type"], key, defaultValue); },
   },
   "global": {
      "links_action": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["links_action"], key, defaultValue); },
      "otherLinks_action": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["otherLinks_action"], key, defaultValue); },
      "structure_actionPosition": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["structure_actionPosition"], key, defaultValue); },
      "structure_dashboardBlockType": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["structure_dashboardBlockType"], key, defaultValue); },
      "structure_type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["structure_type"], key, defaultValue); },
      "alerts_navigate": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["alerts_navigate"], key, defaultValue); },
      "field_actionPosition": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["field_actionPosition"], key, defaultValue); },
      "field_dashboardBlockType": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["field_dashboardBlockType"], key, defaultValue); },
      "field_type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["field_type"], key, defaultValue); },
      "langs_langs": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["langs_langs"], key, defaultValue); },
      "mainStructure_actionPosition": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["mainStructure_actionPosition"], key, defaultValue); },
      "mainStructure_dashboardBlockType": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["mainStructure_dashboardBlockType"], key, defaultValue); },
      "mainStructure_type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["mainStructure_type"], key, defaultValue); },
      "subStructure_actionPosition": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["subStructure_actionPosition"], key, defaultValue); },
      "subStructure_dashboardBlockType": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["subStructure_dashboardBlockType"], key, defaultValue); },
      "subStructure_type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["subStructure_type"], key, defaultValue); },
      "sort_direction": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["sort_direction"], key, defaultValue); },
      "filter_actionPosition": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["filter_actionPosition"], key, defaultValue); },
      "filter_dashboardBlockType": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["filter_dashboardBlockType"], key, defaultValue); },
      "filter_type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["filter_type"], key, defaultValue); },
      "args_actionPosition": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["args_actionPosition"], key, defaultValue); },
      "args_dashboardBlockType": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["args_dashboardBlockType"], key, defaultValue); },
      "args_type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["args_type"], key, defaultValue); },
      "localizations_targetLang": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.global["localizations_targetLang"], key, defaultValue); },
   },
   "shmChannelMessages": {
      "type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.shmChannelMessages["type"], key, defaultValue); },
      "style": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.shmChannelMessages["style"], key, defaultValue); },
   },
   "shmChannels": {
      "role": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.shmChannels["role"], key, defaultValue); },
      "type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper.shmChannels["type"], key, defaultValue); },
   },
   "_shm_exports": {
      "type": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper._shm_exports["type"], key, defaultValue); },
      "status": (key: string | undefined | null, defaultValue?: any) => { return enumHelperValue(enumHelper._shm_exports["status"], key, defaultValue); },
   },
};
export const enumHelperValue = (
   values: { [key: string]: string },
   key: string | undefined | null,
   defaultValue: any
) => {
   if (!key) return defaultValue;
   return values[key!] ?? defaultValue;
}