import * as plugins from '../../plugins.js';
import { HuaweiLteMapper } from './huawei_lte.mapper.js';
import type {
  IHuaweiLteClientLike,
  IHuaweiLteCommandRequest,
  IHuaweiLteConfig,
  IHuaweiLteRawData,
  IHuaweiLteRefreshResult,
  IHuaweiLteSnapshot,
  IHuaweiLteValueMap,
  THuaweiLteRawDataKey,
} from './huawei_lte.types.js';
import { huaweiLteDefaultHttpPort, huaweiLteDefaultHttpsPort, huaweiLteDefaultTimeoutMs } from './huawei_lte.types.js';

export class HuaweiLteApiError extends Error {}
export class HuaweiLteApiConnectionError extends HuaweiLteApiError {}
export class HuaweiLteApiAuthorizationError extends HuaweiLteApiError {}
export class HuaweiLteApiResponseError extends HuaweiLteApiError {
  constructor(messageArg: string, public readonly code?: number) {
    super(messageArg);
  }
}
export class HuaweiLteUnsupportedCommandError extends HuaweiLteApiError {}

const rawResourceDefinitions: Array<{ key: THuaweiLteRawDataKey; endpoint: string }> = [
  { key: 'deviceInformation', endpoint: 'device/information' },
  { key: 'deviceBasicInformation', endpoint: 'device/basic_information' },
  { key: 'deviceSignal', endpoint: 'device/signal' },
  { key: 'dialupMobileDataswitch', endpoint: 'dialup/mobile-dataswitch' },
  { key: 'monitoringMonthStatistics', endpoint: 'monitoring/month_statistics' },
  { key: 'monitoringCheckNotifications', endpoint: 'monitoring/check-notifications' },
  { key: 'monitoringStatus', endpoint: 'monitoring/status' },
  { key: 'monitoringTrafficStatistics', endpoint: 'monitoring/traffic-statistics' },
  { key: 'netCurrentPlmn', endpoint: 'net/current-plmn' },
  { key: 'netNetMode', endpoint: 'net/net-mode' },
  { key: 'smsSmsCount', endpoint: 'sms/sms-count' },
  { key: 'lanHostInfo', endpoint: 'lan/HostInfo' },
  { key: 'wlanHostList', endpoint: 'wlan/host-list' },
  { key: 'wlanWifiFeatureSwitch', endpoint: 'wlan/wifi-feature-switch' },
  { key: 'wlanMultiBasicSettings', endpoint: 'wlan/multi-basic-settings' },
];

const resourceEndpointByKey = Object.fromEntries(rawResourceDefinitions.map((definitionArg) => [definitionArg.key, definitionArg.endpoint])) as Record<THuaweiLteRawDataKey, string>;

export class HuaweiLteClient {
  private currentSnapshot?: IHuaweiLteSnapshot;
  private suspended = false;

  constructor(private readonly config: IHuaweiLteConfig) {}

  public async getSnapshot(forceRefreshArg = false): Promise<IHuaweiLteSnapshot> {
    if (!forceRefreshArg && this.currentSnapshot) {
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (this.suspended && this.currentSnapshot) {
      return this.cloneSnapshot({ ...this.currentSnapshot, suspended: true });
    }

    if (!forceRefreshArg && this.config.snapshot) {
      this.currentSnapshot = HuaweiLteMapper.toSnapshot({ config: this.config, source: 'snapshot', online: this.config.snapshot.online, suspended: this.suspended });
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (this.config.client) {
      try {
        this.currentSnapshot = await this.snapshotFromClient(this.config.client);
      } catch (errorArg) {
        this.currentSnapshot = this.offlineSnapshot(this.errorMessage(errorArg));
      }
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (!forceRefreshArg && this.hasManualData()) {
      this.currentSnapshot = HuaweiLteMapper.toSnapshot({
        config: this.config,
        rawData: this.config.rawData,
        online: this.config.online ?? true,
        source: 'manual',
        suspended: this.suspended,
      });
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (this.config.url || this.config.host) {
      try {
        this.currentSnapshot = await this.fetchSnapshot();
      } catch (errorArg) {
        this.currentSnapshot = this.offlineSnapshot(this.errorMessage(errorArg));
      }
      return this.cloneSnapshot(this.currentSnapshot);
    }

    this.currentSnapshot = this.offlineSnapshot('No Huawei LTE local HTTP endpoint, client, snapshot, or raw data is configured.');
    return this.cloneSnapshot(this.currentSnapshot);
  }

  public async refresh(): Promise<IHuaweiLteRefreshResult> {
    try {
      this.currentSnapshot = undefined;
      const snapshot = await this.getSnapshot(Boolean(this.config.url || this.config.host || this.config.client));
      const success = snapshot.online && !snapshot.error && snapshot.source !== 'runtime';
      return { success, snapshot, error: success ? undefined : snapshot.error, data: { source: snapshot.source } };
    } catch (errorArg) {
      const error = this.errorMessage(errorArg);
      const snapshot = this.offlineSnapshot(error);
      this.currentSnapshot = snapshot;
      return { success: false, snapshot: this.cloneSnapshot(snapshot), error };
    }
  }

  public async fetchSnapshot(): Promise<IHuaweiLteSnapshot> {
    const session = await this.createHttpSession();
    const rawData = await this.fetchRawData(session);
    return HuaweiLteMapper.toSnapshot({ config: this.config, rawData, online: true, source: 'http', suspended: this.suspended });
  }

  public async execute(commandArg: IHuaweiLteCommandRequest): Promise<unknown> {
    if (this.config.commandExecutor) {
      return this.config.commandExecutor.execute(commandArg);
    }
    if (this.config.client?.execute) {
      return this.config.client.execute(commandArg);
    }
    if (commandArg.action === 'refresh') {
      return (await this.refresh()).snapshot;
    }
    if (commandArg.action === 'suspend_integration') {
      this.suspended = true;
      return { suspended: true };
    }
    if (commandArg.action === 'resume_integration') {
      this.suspended = false;
      return { suspended: false };
    }
    if (!this.config.url && !this.config.host) {
      throw new HuaweiLteApiConnectionError('Huawei LTE commands require config.url/config.host, an injected client.execute, or commandExecutor. Static snapshots/manual data are read-only.');
    }

    const session = await this.createHttpSession();
    switch (commandArg.action) {
      case 'set_mobile_dataswitch':
        if (commandArg.enabled === undefined) {
          throw new HuaweiLteUnsupportedCommandError('Huawei LTE mobile data switch commands require an enabled boolean.');
        }
        return session.postSet('dialup/mobile-dataswitch', { dataswitch: commandArg.enabled ? 1 : 0 });
      case 'set_wifi_guest_network':
        if (commandArg.enabled === undefined) {
          throw new HuaweiLteUnsupportedCommandError('Huawei LTE guest WiFi switch commands require an enabled boolean.');
        }
        return this.setWifiGuestNetwork(session, commandArg.enabled);
      case 'reboot':
        return session.postSet('device/control', { Control: 1 });
      case 'clear_traffic':
        return session.postSet('monitoring/clear-traffic', { ClearTraffic: 1 });
      case 'send_sms':
        return this.sendSms(session, commandArg);
      case 'set_net_mode':
        if (!commandArg.networkMode) {
          throw new HuaweiLteUnsupportedCommandError('Huawei LTE network mode commands require networkMode.');
        }
        return session.postSet('net/net-mode', {
          NetworkMode: commandArg.networkMode,
          NetworkBand: commandArg.networkBand || '3fffffff',
          LTEBand: commandArg.lteBand || '7fffffffffffffff',
        });
      default:
        throw new HuaweiLteUnsupportedCommandError(`Unsupported Huawei LTE command: ${commandArg.action}`);
    }
  }

  public async destroy(): Promise<void> {
    await this.config.client?.destroy?.();
  }

  private async createHttpSession(): Promise<HuaweiLteHttpSession> {
    const session = new HuaweiLteHttpSession(this.config);
    await session.initialize();
    return session;
  }

  private async fetchRawData(sessionArg: HuaweiLteHttpSession): Promise<IHuaweiLteRawData> {
    const rawData: IHuaweiLteRawData = { resources: {}, errors: {}, fetchedAt: new Date().toISOString() };
    for (const definition of rawResourceDefinitions) {
      try {
        const data = await sessionArg.getResource(definition.endpoint);
        rawData.resources![definition.key] = data;
        (rawData as Record<string, unknown>)[definition.key] = data;
      } catch (errorArg) {
        rawData.errors![definition.key] = this.errorMessage(errorArg);
      }
    }

    if (!rawData.deviceInformation && rawData.deviceBasicInformation) {
      rawData.deviceInformation = rawData.deviceBasicInformation;
    }
    if (rawData.wlanMultiBasicSettings) {
      const guest = this.guestNetworkSettings(rawData.wlanMultiBasicSettings);
      if (guest) {
        rawData.wlanWifiGuestNetworkSwitch = guest;
      }
    }
    const hasData = Object.keys(rawData.resources || {}).length > 0;
    if (!hasData) {
      throw new HuaweiLteApiConnectionError(`No Huawei LTE API endpoints returned usable data from ${sessionArg.baseUrl}.`);
    }
    return rawData;
  }

  private async snapshotFromClient(clientArg: IHuaweiLteClientLike): Promise<IHuaweiLteSnapshot> {
    if (clientArg.getSnapshot) {
      const result = await clientArg.getSnapshot();
      if (this.isSnapshot(result)) {
        return HuaweiLteMapper.toSnapshot({ config: { ...this.config, snapshot: result }, source: 'client', online: result.online, suspended: this.suspended });
      }
      return HuaweiLteMapper.toSnapshot({ config: this.config, rawData: result, online: true, source: 'client', suspended: this.suspended });
    }
    if (clientArg.getRawData) {
      return HuaweiLteMapper.toSnapshot({ config: this.config, rawData: await clientArg.getRawData(), online: true, source: 'client', suspended: this.suspended });
    }
    if (clientArg.getResource) {
      const rawData: IHuaweiLteRawData = { resources: {}, fetchedAt: new Date().toISOString() };
      for (const definition of rawResourceDefinitions) {
        try {
          const data = await clientArg.getResource(definition.endpoint);
          rawData.resources![definition.key] = data;
          (rawData as Record<string, unknown>)[definition.key] = data;
        } catch {
          // Injected resource clients may expose only a subset of endpoints.
        }
      }
      return HuaweiLteMapper.toSnapshot({ config: this.config, rawData, online: true, source: 'client', suspended: this.suspended });
    }
    throw new HuaweiLteApiConnectionError('Huawei LTE client must expose getSnapshot(), getRawData(), getResource(), or execute().');
  }

  private async setWifiGuestNetwork(sessionArg: HuaweiLteHttpSession, enabledArg: boolean): Promise<unknown> {
    const settings = await sessionArg.getResource('wlan/multi-basic-settings');
    const ssids = this.ssidList(settings).map((itemArg) => ({ ...itemArg }));
    const guest = ssids.find((itemArg) => String(itemArg.wifiisguestnetwork || '') === '1');
    if (!guest) {
      throw new HuaweiLteUnsupportedCommandError('Huawei LTE guest WiFi network settings are not available on this device.');
    }
    guest.WifiEnable = enabledArg ? '1' : '0';
    return sessionArg.postSet('wlan/multi-basic-settings', { Ssids: { Ssid: ssids }, WifiRestart: 1 });
  }

  private async sendSms(sessionArg: HuaweiLteHttpSession, commandArg: IHuaweiLteCommandRequest): Promise<unknown> {
    const phoneNumbers = commandArg.phoneNumbers?.filter(Boolean) || [];
    const message = commandArg.message || '';
    if (!phoneNumbers.length || !message) {
      throw new HuaweiLteUnsupportedCommandError('Huawei LTE SMS commands require phoneNumbers and message.');
    }
    return sessionArg.postSet('sms/send-sms', {
      Index: commandArg.smsIndex ?? -1,
      Phones: { Phone: phoneNumbers },
      Sca: commandArg.sca || undefined,
      Content: message,
      Length: message.length,
      Reserved: 1,
      Date: this.huaweiDate(new Date()),
    });
  }

  private guestNetworkSettings(settingsArg: IHuaweiLteValueMap): IHuaweiLteValueMap | undefined {
    return this.ssidList(settingsArg).find((itemArg) => String(itemArg.wifiisguestnetwork || '') === '1');
  }

  private ssidList(settingsArg: IHuaweiLteValueMap): IHuaweiLteValueMap[] {
    const ssids = this.objectValue(settingsArg.Ssids)?.Ssid;
    return this.asArray(ssids).map((itemArg) => this.objectValue(itemArg)).filter(Boolean) as IHuaweiLteValueMap[];
  }

  private hasManualData(): boolean {
    return Boolean(this.config.snapshot || this.config.rawData);
  }

  private offlineSnapshot(errorArg: string): IHuaweiLteSnapshot {
    return HuaweiLteMapper.toSnapshot({ config: this.config, online: false, source: 'runtime', suspended: this.suspended, error: errorArg });
  }

  private isSnapshot(valueArg: unknown): valueArg is IHuaweiLteSnapshot {
    return Boolean(valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'connection' in valueArg && 'online' in valueArg);
  }

  private objectValue(valueArg: unknown): IHuaweiLteValueMap | undefined {
    return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as IHuaweiLteValueMap : undefined;
  }

  private asArray(valueArg: unknown): unknown[] {
    if (Array.isArray(valueArg)) {
      return valueArg;
    }
    if (valueArg === undefined || valueArg === null || valueArg === '') {
      return [];
    }
    return [valueArg];
  }

  private huaweiDate(valueArg: Date): string {
    const pad = (value: number): string => String(value).padStart(2, '0');
    return `${valueArg.getUTCFullYear()}-${pad(valueArg.getUTCMonth() + 1)}-${pad(valueArg.getUTCDate())} ${pad(valueArg.getUTCHours())}:${pad(valueArg.getUTCMinutes())}:${pad(valueArg.getUTCSeconds())}`;
  }

  private errorMessage(errorArg: unknown): string {
    return errorArg instanceof Error ? errorArg.message : String(errorArg);
  }

  private cloneSnapshot(snapshotArg: IHuaweiLteSnapshot): IHuaweiLteSnapshot {
    return JSON.parse(JSON.stringify(snapshotArg)) as IHuaweiLteSnapshot;
  }
}

class HuaweiLteHttpSession {
  public readonly baseUrl: string;
  private requestVerificationTokens: string[] = [];
  private cookies = new Map<string, string>();
  private loggedIn = false;

  constructor(private readonly config: IHuaweiLteConfig) {
    this.baseUrl = this.resolveBaseUrl(config);
  }

  public async initialize(): Promise<void> {
    const homepage = await this.requestText('', { prefix: '', allowHtml: true }).catch(() => '');
    this.requestVerificationTokens = this.extractCsrfTokens(homepage);
    if (this.requestVerificationTokens.length) {
      return;
    }
    const token = await this.tryGetToken('webserver/token', 'token') || await this.tryGetToken('webserver/SesTokInfo', 'TokInfo');
    if (token) {
      this.requestVerificationTokens.push(token);
    }
  }

  public async getResource(endpointArg: string): Promise<IHuaweiLteValueMap> {
    const data = await this.requestParsed(endpointArg, { method: 'GET' });
    return this.objectResponse(data);
  }

  public async postSet(endpointArg: string, dataArg: IHuaweiLteValueMap | number): Promise<unknown> {
    await this.loginIfConfigured();
    return this.requestParsed(endpointArg, { method: 'POST', body: this.requestXml(dataArg) });
  }

  private async loginIfConfigured(): Promise<void> {
    if (this.loggedIn || this.config.unauthenticatedMode || (!this.config.username && !this.config.password)) {
      return;
    }
    const username = this.config.username || 'admin';
    const state = await this.getResource('user/state-login').catch(() => undefined);
    if (String(state?.State) === '0') {
      this.loggedIn = true;
      return;
    }
    const passwordType = Number(state?.password_type ?? 0);
    const password = this.encodedPassword(username, this.config.password || '', passwordType);
    const result = await this.requestParsed('user/login', {
      method: 'POST',
      body: this.requestXml({ Username: username, Password: password, password_type: passwordType }),
      refreshCsrf: true,
    });
    this.loggedIn = result === 'OK' || (typeof result === 'object' && result !== null);
  }

  private async tryGetToken(endpointArg: string, keyArg: string): Promise<string | undefined> {
    try {
      const data = await this.getResource(endpointArg);
      const token = data[keyArg];
      return typeof token === 'string' && token ? token : undefined;
    } catch {
      return undefined;
    }
  }

  private async requestParsed(endpointArg: string, optionsArg: { method: 'GET' | 'POST'; body?: string; refreshCsrf?: boolean }): Promise<unknown> {
    const text = await this.requestText(endpointArg, optionsArg);
    const data = this.parseResponse(text);
    return this.checkResponse(data);
  }

  private async requestText(endpointArg: string, optionsArg: { method?: 'GET' | 'POST'; body?: string; prefix?: string; refreshCsrf?: boolean; allowHtml?: boolean } = {}): Promise<string> {
    const method = optionsArg.method || 'GET';
    const headers: Record<string, string> = {};
    if (method === 'POST') {
      headers['content-type'] = 'application/xml';
    }
    const token = this.tokenForRequest(method);
    if (token) {
      headers.__RequestVerificationToken = token;
    }
    const cookie = this.cookieHeader();
    if (cookie) {
      headers.cookie = cookie;
    }

    let response: Response;
    const url = this.buildUrl(endpointArg, optionsArg.prefix ?? 'api');
    try {
      response = await globalThis.fetch(url, {
        method,
        headers,
        body: optionsArg.body,
        signal: AbortSignal.timeout(this.config.timeoutMs || huaweiLteDefaultTimeoutMs),
      });
    } catch (errorArg) {
      throw new HuaweiLteApiConnectionError(`Connection to ${url} failed: ${this.errorMessage(errorArg)}`);
    }

    this.storeCookies(response);
    if (optionsArg.refreshCsrf) {
      this.requestVerificationTokens = [];
    }
    this.storeTokens(response);

    if (!response.ok) {
      const body = await response.text().catch(() => '');
      throw new HuaweiLteApiConnectionError(`Huawei LTE endpoint ${url} failed with HTTP ${response.status}${body ? `: ${body}` : ''}`);
    }
    const text = await response.text();
    if (!optionsArg.allowHtml && /^\s*<!doctype html|<html[\s>]/i.test(text)) {
      throw new HuaweiLteApiResponseError('Huawei LTE endpoint returned HTML instead of API data.', 100002);
    }
    return text;
  }

  private parseResponse(textArg: string): unknown {
    const text = textArg.trim();
    if (!text) {
      return {};
    }
    if (text.startsWith('{') || text.startsWith('[')) {
      return JSON.parse(text) as unknown;
    }
    return parseXml(text);
  }

  private checkResponse(dataArg: unknown): unknown {
    const root = this.objectValue(dataArg);
    const payload = root?.response ?? root;
    const error = root?.error;
    if (error && typeof error === 'object') {
      const errorObject = error as Record<string, unknown>;
      const code = Number(errorObject.code);
      const message = typeof errorObject.message === 'string' && errorObject.message ? errorObject.message : this.errorMessageForCode(code);
      if (code === 100003) {
        throw new HuaweiLteApiAuthorizationError(`${code}: ${message}`);
      }
      throw new HuaweiLteApiResponseError(`${code}: ${message}`, code);
    }
    return payload === undefined || payload === null ? {} : payload;
  }

  private objectResponse(valueArg: unknown): IHuaweiLteValueMap {
    if (valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg)) {
      return valueArg as IHuaweiLteValueMap;
    }
    if (typeof valueArg === 'string') {
      return { value: valueArg };
    }
    return {};
  }

  private requestXml(valueArg: IHuaweiLteValueMap | number): string {
    return `<?xml version="1.0" encoding="UTF-8"?><request>${this.xmlValue(valueArg)}</request>`;
  }

  private xmlValue(valueArg: unknown, keyArg?: string): string {
    if (Array.isArray(valueArg)) {
      return valueArg.map((itemArg) => this.xmlValue(itemArg, keyArg)).join('');
    }
    if (keyArg) {
      return `<${keyArg}>${this.xmlValue(valueArg)}</${keyArg}>`;
    }
    if (valueArg && typeof valueArg === 'object') {
      return Object.entries(valueArg as Record<string, unknown>)
        .filter(([, value]) => value !== undefined)
        .map(([key, value]) => this.xmlValue(value, key))
        .join('');
    }
    return escapeXml(valueArg === undefined || valueArg === null ? '' : String(valueArg));
  }

  private encodedPassword(usernameArg: string, passwordArg: string, passwordTypeArg: number): string {
    if (!passwordArg) {
      return '';
    }
    if (passwordTypeArg === 4) {
      const passwordHash = this.base64(this.sha256Hex(passwordArg));
      const token = this.requestVerificationTokens[0] || '';
      return this.base64(this.sha256Hex(`${usernameArg}${passwordHash}${token}`));
    }
    return this.base64(passwordArg);
  }

  private sha256Hex(valueArg: string): string {
    return plugins.crypto.createHash('sha256').update(valueArg, 'utf8').digest('hex');
  }

  private base64(valueArg: string): string {
    return Buffer.from(valueArg, 'utf8').toString('base64');
  }

  private tokenForRequest(methodArg: 'GET' | 'POST'): string | undefined {
    if (!this.requestVerificationTokens.length) {
      return undefined;
    }
    if (methodArg === 'POST' && this.requestVerificationTokens.length > 1) {
      return this.requestVerificationTokens.shift();
    }
    return this.requestVerificationTokens[0];
  }

  private storeTokens(responseArg: Response): void {
    const tokenOne = responseArg.headers.get('__RequestVerificationTokenone');
    const tokenTwo = responseArg.headers.get('__RequestVerificationTokentwo');
    const token = responseArg.headers.get('__RequestVerificationToken');
    if (tokenOne) {
      this.requestVerificationTokens.push(tokenOne);
      if (tokenTwo) {
        this.requestVerificationTokens.push(tokenTwo);
      }
    } else if (token) {
      this.requestVerificationTokens.push(token);
    }
  }

  private storeCookies(responseArg: Response): void {
    const headers = responseArg.headers as Headers & { getSetCookie?: () => string[] };
    const setCookies = headers.getSetCookie ? headers.getSetCookie() : splitSetCookieHeader(responseArg.headers.get('set-cookie'));
    for (const cookie of setCookies) {
      const [pair] = cookie.split(';', 1);
      const index = pair.indexOf('=');
      if (index > 0) {
        this.cookies.set(pair.slice(0, index), pair.slice(index + 1));
      }
    }
  }

  private cookieHeader(): string | undefined {
    return this.cookies.size ? [...this.cookies.entries()].map(([key, value]) => `${key}=${value}`).join('; ') : undefined;
  }

  private extractCsrfTokens(htmlArg: string): string[] {
    const tokens: string[] = [];
    const pattern = /name=["']csrf_token["']\s+content=["']([^"']+)["']/gi;
    let match: RegExpExecArray | null;
    while ((match = pattern.exec(htmlArg))) {
      tokens.push(match[1]);
    }
    return tokens;
  }

  private buildUrl(endpointArg: string, prefixArg: string): string {
    if (/^https?:\/\//i.test(endpointArg)) {
      return endpointArg;
    }
    const normalizedEndpoint = endpointArg.replace(/^\/+/, '');
    const prefix = prefixArg ? `${prefixArg.replace(/^\/+|\/+$/g, '')}/` : '';
    return new URL(`${prefix}${normalizedEndpoint}`, this.baseUrl).toString();
  }

  private resolveBaseUrl(configArg: IHuaweiLteConfig): string {
    const configured = configArg.url || configArg.host || '192.168.8.1';
    if (/^[a-z][a-z0-9+.-]*:\/\//i.test(configured)) {
      const url = new URL(configured);
      return url.toString().endsWith('/') ? url.toString() : `${url.toString()}/`;
    }
    const ssl = configArg.ssl ?? false;
    const defaultPort = ssl ? huaweiLteDefaultHttpsPort : huaweiLteDefaultHttpPort;
    const port = configArg.port && configArg.port !== defaultPort ? `:${configArg.port}` : '';
    return `${ssl ? 'https' : 'http'}://${this.hostForUrl(configured)}${port}/`;
  }

  private errorMessageForCode(codeArg: number): string {
    const messages: Record<number, string> = {
      100001: 'Unknown',
      100002: 'No support',
      100003: 'No rights (needs login)',
      100004: 'System busy',
      100005: 'Request format error',
      125002: 'Session error',
      125003: 'Wrong session token',
    };
    return messages[codeArg] || 'Unknown';
  }

  private objectValue(valueArg: unknown): Record<string, unknown> | undefined {
    return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : undefined;
  }

  private hostForUrl(hostArg: string): string {
    return hostArg.includes(':') && !hostArg.startsWith('[') ? `[${hostArg}]` : hostArg;
  }

  private errorMessage(errorArg: unknown): string {
    return errorArg instanceof Error ? errorArg.message : String(errorArg);
  }
}

const parseXml = (xmlArg: string): unknown => {
  interface IXmlNode {
    name: string;
    value: Record<string, unknown>;
    text: string;
  }
  const root: IXmlNode = { name: '', value: {}, text: '' };
  const stack: IXmlNode[] = [root];
  const tokenPattern = /<\?[^>]*\?>|<!--[\s\S]*?-->|<!\[CDATA\[([\s\S]*?)\]\]>|<([A-Za-z0-9_:-]+)(?:\s[^>]*)?\/>|<([A-Za-z0-9_:-]+)(?:\s[^>]*)?>|<\/([A-Za-z0-9_:-]+)>|([^<]+)/g;
  let match: RegExpExecArray | null;
  while ((match = tokenPattern.exec(xmlArg))) {
    if (match[1] !== undefined) {
      stack[stack.length - 1].text += match[1];
      continue;
    }
    if (match[2]) {
      addXmlChild(stack[stack.length - 1], match[2], '');
      continue;
    }
    if (match[3]) {
      stack.push({ name: match[3], value: {}, text: '' });
      continue;
    }
    if (match[4]) {
      const node = stack.pop();
      if (node && stack.length) {
        addXmlChild(stack[stack.length - 1], node.name, Object.keys(node.value).length ? node.value : decodeXml(node.text.trim()));
      }
      continue;
    }
    if (match[5]) {
      stack[stack.length - 1].text += decodeXml(match[5]);
    }
  }
  return root.value;
};

const addXmlChild = (nodeArg: { value: Record<string, unknown> }, keyArg: string, valueArg: unknown): void => {
  const current = nodeArg.value[keyArg];
  if (current === undefined) {
    nodeArg.value[keyArg] = valueArg;
  } else if (Array.isArray(current)) {
    current.push(valueArg);
  } else {
    nodeArg.value[keyArg] = [current, valueArg];
  }
};

const escapeXml = (valueArg: string): string => valueArg
  .replace(/&/g, '&amp;')
  .replace(/</g, '&lt;')
  .replace(/>/g, '&gt;')
  .replace(/"/g, '&quot;')
  .replace(/'/g, '&apos;');

const decodeXml = (valueArg: string): string => valueArg
  .replace(/&lt;/g, '<')
  .replace(/&gt;/g, '>')
  .replace(/&quot;/g, '"')
  .replace(/&apos;/g, "'")
  .replace(/&amp;/g, '&');

const splitSetCookieHeader = (valueArg: string | null): string[] => {
  if (!valueArg) {
    return [];
  }
  return valueArg.split(/,(?=\s*[^;,]+=)/g).map((itemArg) => itemArg.trim()).filter(Boolean);
};

export const huaweiLteResourceEndpointByKey = resourceEndpointByKey;
