import type {
  ISoundtouchCommandRequest,
  ISoundtouchConfig,
  ISoundtouchContentItem,
  ISoundtouchDeviceInfo,
  ISoundtouchPreset,
  ISoundtouchRawCommandRequest,
  ISoundtouchSnapshot,
  ISoundtouchStatus,
  ISoundtouchVolume,
  ISoundtouchZoneMember,
  ISoundtouchZoneStatus,
  TSoundtouchCommand,
} from './soundtouch.types.js';
import { soundtouchDefaultDlnaPort, soundtouchDefaultPort, soundtouchDefaultSourceList } from './soundtouch.types.js';

const keyCommands: Record<Exclude<TSoundtouchCommand, 'turn_on' | 'turn_off' | 'set_volume' | 'mute' | 'select_source' | 'play_media' | 'play_preset'>, string> = {
  play: 'PLAY',
  pause: 'PAUSE',
  play_pause: 'PLAY_PAUSE',
  previous_track: 'PREV_TRACK',
  next_track: 'NEXT_TRACK',
  volume_up: 'VOLUME_UP',
  volume_down: 'VOLUME_DOWN',
};

export class SoundtouchClient {
  constructor(private readonly config: ISoundtouchConfig) {}

  public async getSnapshot(): Promise<ISoundtouchSnapshot> {
    if (this.config.snapshot) {
      return this.normalizeSnapshot(this.cloneValue(this.config.snapshot));
    }
    if (!this.config.host && !this.config.commandExecutor) {
      return this.normalizeSnapshot(this.manualSnapshot(false, 'SoundTouch refresh requires config.host, config.snapshot, or commandExecutor.'));
    }

    const infoXml = await this.fetchText('/info');
    const deviceInfo = this.parseDeviceInfo(infoXml);
    const [status, volume, presets, zone] = await Promise.all([
      this.optionalText('/now_playing').then((xmlArg) => xmlArg ? this.parseStatus(xmlArg) : undefined),
      this.optionalText('/volume').then((xmlArg) => xmlArg ? this.parseVolume(xmlArg) : undefined),
      this.optionalText('/presets').then((xmlArg) => xmlArg ? this.parsePresets(xmlArg) : []),
      this.optionalText('/getZone').then((xmlArg) => xmlArg ? this.parseZoneStatus(xmlArg) : undefined),
    ]);

    return this.normalizeSnapshot({
      config: deviceInfo,
      status,
      volume,
      presets,
      zone,
      sourceList: this.config.sourceList,
      available: true,
      lastUpdated: new Date().toISOString(),
    });
  }

  public async validateConnection(): Promise<ISoundtouchSnapshot> {
    const snapshot = await this.getSnapshot();
    if (!snapshot.config.deviceId && !snapshot.config.name) {
      throw new Error('SoundTouch device did not provide a device identifier.');
    }
    return snapshot;
  }

  public async execute(requestArg: ISoundtouchCommandRequest): Promise<unknown> {
    this.ensureCommandTransport();

    if (requestArg.command === 'turn_on') {
      return this.setPower(true);
    }
    if (requestArg.command === 'turn_off') {
      return this.setPower(false);
    }
    if (requestArg.command in keyCommands) {
      return this.sendKey(keyCommands[requestArg.command as keyof typeof keyCommands], requestArg.command);
    }
    if (requestArg.command === 'set_volume') {
      return this.setVolume(requestArg);
    }
    if (requestArg.command === 'mute') {
      return this.setMute(requestArg.muted);
    }
    if (requestArg.command === 'select_source') {
      return this.selectSource(requestArg.source);
    }
    if (requestArg.command === 'play_preset') {
      return this.playPreset(requestArg);
    }
    if (requestArg.command === 'play_media') {
      return this.playMedia(requestArg);
    }

    throw new Error(`Unsupported SoundTouch command: ${requestArg.command}`);
  }

  public async destroy(): Promise<void> {}

  private async setPower(poweredArg: boolean): Promise<unknown> {
    const snapshot = await this.getSnapshot().catch(() => undefined);
    const isStandby = snapshot?.status?.source === 'STANDBY';
    if (poweredArg && snapshot?.status && !isStandby) {
      return { skipped: true, reason: 'SoundTouch device is already on.' };
    }
    if (!poweredArg && isStandby) {
      return { skipped: true, reason: 'SoundTouch device is already off.' };
    }
    return this.sendKey('POWER', poweredArg ? 'turn_on' : 'turn_off');
  }

  private async setVolume(requestArg: ISoundtouchCommandRequest): Promise<string> {
    const volume = this.volumePercent(requestArg);
    return this.requestText('POST', '/volume', 'set_volume', `<volume>${volume}</volume>`, { 'content-type': 'text/xml; charset="utf-8"' });
  }

  private async setMute(mutedArg: boolean | undefined): Promise<unknown> {
    if (typeof mutedArg === 'boolean') {
      const snapshot = await this.getSnapshot().catch(() => undefined);
      if (typeof snapshot?.volume?.muted === 'boolean' && snapshot.volume.muted === mutedArg) {
        return { skipped: true, reason: `SoundTouch mute is already ${mutedArg ? 'on' : 'off'}.` };
      }
    }
    return this.sendKey('MUTE', 'mute');
  }

  private async selectSource(sourceArg: string | undefined): Promise<string> {
    const source = sourceArg?.toUpperCase();
    if (source !== 'AUX' && source !== 'BLUETOOTH') {
      throw new Error('SoundTouch select_source supports AUX or BLUETOOTH.');
    }
    return this.sendSelectXml(this.contentItemXml({ source, sourceAccount: source === 'AUX' ? 'AUX' : undefined }), 'select_source');
  }

  private async playPreset(requestArg: ISoundtouchCommandRequest): Promise<string> {
    const preset = requestArg.contentItem ? { presetId: requestArg.presetId || '', ...requestArg.contentItem } : await this.findPreset(requestArg);
    const body = preset.sourceXml || this.contentItemXml(preset);
    return this.sendSelectXml(body, 'play_preset');
  }

  private async playMedia(requestArg: ISoundtouchCommandRequest): Promise<unknown> {
    const mediaId = requestArg.url || requestArg.mediaId;
    if (mediaId?.toLowerCase().startsWith('http://')) {
      return this.playUrl(mediaId);
    }
    if (mediaId?.toLowerCase().startsWith('https://')) {
      throw new Error('SoundTouch play_media supports only http:// URLs for DLNA playback.');
    }
    return this.playPreset({ ...requestArg, presetId: requestArg.presetId || mediaId });
  }

  private async playUrl(urlArg: string): Promise<string> {
    const body = `<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><CurrentURIMetaData></CurrentURIMetaData><CurrentURI>${escapeXml(urlArg)}</CurrentURI></u:SetAVTransportURI></s:Body></s:Envelope>`;
    return this.requestText('POST', '/AVTransport/Control', 'play_media', body, {
      'user-agent': 'smarthome.exchange',
      accept: '*/*',
      'content-type': 'text/xml; charset="utf-8"',
      host: this.config.host ? `${this.config.host}:${this.dlnaPort()}` : `:${this.dlnaPort()}`,
      soapaction: 'urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI',
    }, this.dlnaPort());
  }

  private async sendKey(keyArg: string, commandArg: TSoundtouchCommand): Promise<string[]> {
    const body = (stateArg: 'press' | 'release') => `<key state="${stateArg}" sender="Gabbo">${keyArg}</key>`;
    return [
      await this.requestText('POST', '/key', commandArg, body('press'), { 'content-type': 'text/xml; charset="utf-8"' }),
      await this.requestText('POST', '/key', commandArg, body('release'), { 'content-type': 'text/xml; charset="utf-8"' }),
    ];
  }

  private async sendSelectXml(bodyArg: string, commandArg: TSoundtouchCommand): Promise<string> {
    return this.requestText('POST', '/select', commandArg, bodyArg, { 'content-type': 'text/xml; charset="utf-8"' });
  }

  private async findPreset(requestArg: ISoundtouchCommandRequest): Promise<ISoundtouchPreset> {
    const snapshot = await this.getSnapshot();
    const preset = (snapshot.presets || []).find((presetArg) => {
      return Boolean(
        requestArg.presetId && String(presetArg.presetId) === String(requestArg.presetId)
        || requestArg.presetId && presetArg.name === requestArg.presetId
        || requestArg.presetName && presetArg.name === requestArg.presetName
      );
    });
    if (!preset) {
      throw new Error('SoundTouch play_preset requires a known preset id or name.');
    }
    return preset;
  }

  private contentItemXml(itemArg: ISoundtouchContentItem): string {
    if (!itemArg.source) {
      throw new Error('SoundTouch ContentItem requires source.');
    }
    const attributes: Record<string, string | undefined> = {
      source: itemArg.source,
      type: itemArg.type,
      location: itemArg.location,
      sourceAccount: itemArg.sourceAccount,
      isPresetable: typeof itemArg.isPresetable === 'boolean' ? String(itemArg.isPresetable) : undefined,
    };
    const attrs = Object.entries(attributes)
      .filter((entryArg): entryArg is [string, string] => typeof entryArg[1] === 'string' && entryArg[1] !== '')
      .map(([keyArg, valueArg]) => `${keyArg}="${escapeXml(valueArg)}"`)
      .join(' ');
    const itemName = itemArg.name ? `<itemName>${escapeXml(itemArg.name)}</itemName>` : '';
    return `<ContentItem ${attrs}>${itemName}</ContentItem>`;
  }

  private async fetchText(pathArg: string): Promise<string> {
    return this.requestText('GET', pathArg, 'refresh');
  }

  private async optionalText(pathArg: string): Promise<string | undefined> {
    try {
      return await this.fetchText(pathArg);
    } catch {
      return undefined;
    }
  }

  private async requestText(methodArg: 'GET' | 'POST', pathArg: string, commandArg: TSoundtouchCommand | 'refresh', bodyArg?: string, headersArg?: Record<string, string>, portArg = this.port()): Promise<string> {
    const rawRequest = this.rawRequest(methodArg, pathArg, commandArg, bodyArg, headersArg, portArg);
    if (this.config.commandExecutor) {
      return this.executorResultToText(await this.config.commandExecutor.execute(rawRequest));
    }
    if (!rawRequest.host) {
      throw new Error('SoundTouch command transport requires config.host or commandExecutor.');
    }

    const controller = this.config.timeoutMs ? new AbortController() : undefined;
    const timeout = controller ? globalThis.setTimeout(() => controller.abort(), this.config.timeoutMs) : undefined;
    try {
      const response = await globalThis.fetch(rawRequest.url, {
        method: rawRequest.method,
        headers: rawRequest.headers,
        body: rawRequest.body,
        signal: controller?.signal,
      });
      const text = await response.text();
      if (!response.ok) {
        throw new Error(`SoundTouch request ${pathArg} failed with HTTP ${response.status}${text ? `: ${text}` : ''}`);
      }
      return text;
    } finally {
      if (timeout) {
        globalThis.clearTimeout(timeout);
      }
    }
  }

  private rawRequest(methodArg: 'GET' | 'POST', pathArg: string, commandArg: TSoundtouchCommand | 'refresh', bodyArg: string | undefined, headersArg: Record<string, string> | undefined, portArg: number): ISoundtouchRawCommandRequest {
    const path = pathArg.startsWith('/') ? pathArg : `/${pathArg}`;
    return {
      command: commandArg,
      method: methodArg,
      path,
      url: `${this.baseUrl(portArg)}${path}`,
      host: this.config.host,
      port: portArg,
      body: bodyArg,
      headers: headersArg,
    };
  }

  private parseDeviceInfo(xmlArg: string): ISoundtouchDeviceInfo {
    const infoElement = readFirstElement(xmlArg, 'info') || xmlArg;
    const infoAttrs = elementAttributes(infoElement, 'info');
    return {
      deviceId: infoAttrs.deviceID || this.config.deviceId,
      name: readTagValue(infoElement, 'name') || this.config.name,
      type: readTagValue(infoElement, 'type') || this.config.model,
      accountUuid: readTagValue(infoElement, 'margeAccountUUID'),
      moduleType: readTagValue(infoElement, 'moduleType'),
      variant: readTagValue(infoElement, 'variant'),
      variantMode: readTagValue(infoElement, 'variantMode'),
      countryCode: readTagValue(infoElement, 'countryCode'),
      regionCode: readTagValue(infoElement, 'regionCode'),
      host: this.config.host,
      port: this.port(),
      networks: readAllElements(infoElement, 'networkInfo').map((networkArg) => ({
        type: elementAttributes(networkArg, 'networkInfo').type,
        macAddress: readTagValue(networkArg, 'macAddress'),
        ipAddress: readTagValue(networkArg, 'ipAddress'),
      })),
      components: readAllElements(infoElement, 'component').map((componentArg) => ({
        category: readTagValue(componentArg, 'componentCategory'),
        softwareVersion: readTagValue(componentArg, 'softwareVersion'),
        serialNumber: readTagValue(componentArg, 'serialNumber'),
      })),
    };
  }

  private parseStatus(xmlArg: string): ISoundtouchStatus {
    const statusElement = readFirstElement(xmlArg, 'nowPlaying') || xmlArg;
    const statusAttrs = elementAttributes(statusElement, 'nowPlaying');
    const contentElement = readFirstElement(statusElement, 'ContentItem');
    const artElement = readFirstElement(statusElement, 'art');
    const artAttrs = artElement ? elementAttributes(artElement, 'art') : {};
    const timeElement = readFirstElement(statusElement, 'time');
    const timeAttrs = timeElement ? elementAttributes(timeElement, 'time') : {};
    return {
      source: statusAttrs.source,
      contentItem: contentElement ? this.parseContentItem(contentElement) : undefined,
      track: readTagValue(statusElement, 'track'),
      artist: readTagValue(statusElement, 'artist'),
      album: readTagValue(statusElement, 'album'),
      image: artAttrs.artImageStatus === 'IMAGE_PRESENT' ? readTagValue(statusElement, 'art') : undefined,
      duration: numberValue(timeAttrs.total),
      position: numberValue(timeElement ? innerText(timeElement, 'time') : undefined),
      playStatus: readTagValue(statusElement, 'playStatus'),
      shuffleSetting: readTagValue(statusElement, 'shuffleSetting'),
      repeatSetting: readTagValue(statusElement, 'repeatSetting'),
      streamType: readTagValue(statusElement, 'streamType'),
      trackId: readTagValue(statusElement, 'trackID'),
      stationName: readTagValue(statusElement, 'stationName'),
      description: readTagValue(statusElement, 'description'),
      stationLocation: readTagValue(statusElement, 'stationLocation'),
    };
  }

  private parseContentItem(xmlArg: string): ISoundtouchContentItem {
    const attrs = elementAttributes(xmlArg, 'ContentItem');
    return {
      name: readTagValue(xmlArg, 'itemName'),
      source: attrs.source,
      type: attrs.type,
      location: attrs.location,
      sourceAccount: attrs.sourceAccount,
      isPresetable: booleanValue(attrs.isPresetable),
    };
  }

  private parseVolume(xmlArg: string): ISoundtouchVolume {
    return {
      actual: numberValue(readTagValue(xmlArg, 'actualvolume')),
      target: numberValue(readTagValue(xmlArg, 'targetvolume')),
      muted: booleanValue(readTagValue(xmlArg, 'muteenabled')),
    };
  }

  private parsePresets(xmlArg: string): ISoundtouchPreset[] {
    return readAllElements(xmlArg, 'preset').map((presetArg) => {
      const attrs = elementAttributes(presetArg, 'preset');
      const contentElement = readFirstElement(presetArg, 'ContentItem');
      const content = contentElement ? this.parseContentItem(contentElement) : {};
      return {
        ...content,
        presetId: attrs.id || readTagValue(presetArg, 'id') || '',
        sourceXml: contentElement,
      };
    }).filter((presetArg) => presetArg.presetId);
  }

  private parseZoneStatus(xmlArg: string): ISoundtouchZoneStatus | undefined {
    const zoneElement = readFirstElement(xmlArg, 'zone') || xmlArg;
    const members = readAllElements(zoneElement, 'member');
    if (!members.length) {
      return undefined;
    }
    const attrs = elementAttributes(zoneElement, 'zone');
    return {
      masterId: attrs.master,
      masterIp: attrs.senderIPAddress,
      isMaster: attrs.master && this.config.deviceId ? attrs.master === this.config.deviceId : attrs.senderIPAddress === undefined,
      slaves: members.map((memberArg): ISoundtouchZoneMember => {
        const memberAttrs = elementAttributes(memberArg, 'member');
        return {
          deviceId: stripTags(innerXml(memberArg, 'member')).trim() || undefined,
          ipAddress: memberAttrs.ipaddress,
          role: memberAttrs.role,
        };
      }),
    };
  }

  private normalizeSnapshot(snapshotArg: ISoundtouchSnapshot): ISoundtouchSnapshot {
    const config = {
      ...snapshotArg.config,
      deviceId: snapshotArg.config.deviceId || this.config.deviceId || this.config.macAddress || this.config.host,
      name: snapshotArg.config.name || this.config.name || this.config.host || 'Bose SoundTouch',
      type: snapshotArg.config.type || this.config.model || 'SoundTouch',
      host: snapshotArg.config.host || this.config.host,
      port: snapshotArg.config.port || this.config.port || (this.config.host ? soundtouchDefaultPort : undefined),
      networks: snapshotArg.config.networks?.length ? snapshotArg.config.networks : this.config.macAddress || this.config.host ? [{ macAddress: this.config.macAddress, ipAddress: this.config.host }] : [],
    };
    const zone = snapshotArg.zone ? {
      ...snapshotArg.zone,
      isMaster: snapshotArg.zone.masterId && config.deviceId ? snapshotArg.zone.masterId === config.deviceId : snapshotArg.zone.isMaster,
      slaves: snapshotArg.zone.slaves || [],
    } : undefined;
    return {
      ...snapshotArg,
      config,
      presets: snapshotArg.presets || [],
      zone,
      sourceList: snapshotArg.sourceList?.length ? snapshotArg.sourceList : [...soundtouchDefaultSourceList],
      available: snapshotArg.available ?? Boolean(this.config.host || this.config.commandExecutor),
      lastUpdated: snapshotArg.lastUpdated || new Date().toISOString(),
    };
  }

  private manualSnapshot(availableArg: boolean, errorArg?: string): ISoundtouchSnapshot {
    return {
      config: {
        deviceId: this.config.deviceId || this.config.macAddress || this.config.host,
        name: this.config.name || this.config.host || 'Bose SoundTouch',
        type: this.config.model || 'SoundTouch',
        host: this.config.host,
        port: this.config.port || (this.config.host ? soundtouchDefaultPort : undefined),
        networks: this.config.macAddress || this.config.host ? [{ macAddress: this.config.macAddress, ipAddress: this.config.host }] : [],
      },
      presets: [],
      sourceList: this.config.sourceList || [...soundtouchDefaultSourceList],
      available: availableArg,
      lastUpdated: new Date().toISOString(),
      error: errorArg,
    };
  }

  private ensureCommandTransport(): void {
    if (!this.config.host && !this.config.commandExecutor) {
      throw new Error('SoundTouch command transport requires config.host or commandExecutor. Static snapshots are read-only.');
    }
  }

  private async executorResultToText(resultArg: unknown): Promise<string> {
    if (typeof resultArg === 'string') {
      return resultArg;
    }
    if (isFetchResponse(resultArg)) {
      return resultArg.text();
    }
    if (isRecord(resultArg) && typeof resultArg.body === 'string') {
      return resultArg.body;
    }
    if (isRecord(resultArg) && typeof resultArg.text === 'string') {
      return resultArg.text;
    }
    return resultArg === undefined ? '' : JSON.stringify(resultArg);
  }

  private volumePercent(requestArg: ISoundtouchCommandRequest): number {
    const value = requestArg.volume ?? (typeof requestArg.volumeLevel === 'number' ? requestArg.volumeLevel <= 1 ? requestArg.volumeLevel * 100 : requestArg.volumeLevel : undefined);
    if (typeof value !== 'number' || !Number.isFinite(value)) {
      throw new Error('SoundTouch volume command requires volumeLevel or volume.');
    }
    return Math.max(0, Math.min(100, Math.round(value)));
  }

  private port(): number {
    return this.config.port || soundtouchDefaultPort;
  }

  private dlnaPort(): number {
    return this.config.dlnaPort || soundtouchDefaultDlnaPort;
  }

  private baseUrl(portArg: number): string {
    const rawHost = this.config.host || 'localhost';
    if (/^https?:\/\//i.test(rawHost)) {
      const url = new URL(rawHost);
      if (!url.port) {
        url.port = String(portArg);
      }
      return url.origin;
    }
    const host = rawHost.replace(/\/.*$/, '');
    const hostPort = /^([^:]+):(\d+)$/.exec(host);
    if (hostPort) {
      return `http://${hostPort[1]}:${hostPort[2]}`;
    }
    return `http://${formatHost(host)}:${portArg}`;
  }

  private cloneValue<TValue>(valueArg: TValue): TValue {
    return valueArg === undefined ? valueArg : JSON.parse(JSON.stringify(valueArg)) as TValue;
  }
}

const readFirstElement = (xmlArg: string, tagArg: string): string | undefined => {
  const escapedTag = escapeRegExp(tagArg);
  const match = new RegExp(`<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b[^>]*>[\\s\\S]*?<\\/(?:[A-Za-z0-9_]+:)?${escapedTag}>`, 'i').exec(xmlArg)
    || new RegExp(`<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b[^>]*/>`, 'i').exec(xmlArg);
  return match?.[0];
};

const readAllElements = (xmlArg: string, tagArg: string): string[] => {
  const escapedTag = escapeRegExp(tagArg);
  const regex = new RegExp(`<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b[^>]*>[\\s\\S]*?<\\/(?:[A-Za-z0-9_]+:)?${escapedTag}>|<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b[^>]*/>`, 'gi');
  const values: string[] = [];
  let match: RegExpExecArray | null;
  while ((match = regex.exec(xmlArg))) {
    values.push(match[0]);
  }
  return values;
};

const readTagValue = (xmlArg: string, tagArg: string): string | undefined => {
  const element = readFirstElement(xmlArg, tagArg);
  if (!element) {
    return undefined;
  }
  return stripTags(innerXml(element, tagArg)).trim() || undefined;
};

const innerText = (elementArg: string, tagArg: string): string | undefined => {
  return stripTags(innerXml(elementArg, tagArg)).trim() || undefined;
};

const innerXml = (elementArg: string, tagArg: string): string => {
  const escapedTag = escapeRegExp(tagArg);
  return elementArg
    .replace(new RegExp(`^<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b[^>]*>`, 'i'), '')
    .replace(new RegExp(`<\\/(?:[A-Za-z0-9_]+:)?${escapedTag}>$`, 'i'), '')
    .replace(new RegExp(`^<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b[^>]*/>$`, 'i'), '');
};

const elementAttributes = (elementArg: string, tagArg: string): Record<string, string> => {
  const escapedTag = escapeRegExp(tagArg);
  const match = new RegExp(`<(?:[A-Za-z0-9_]+:)?${escapedTag}\\b([^>]*)>`, 'i').exec(elementArg);
  return parseAttributes(match?.[1] || '');
};

const parseAttributes = (valueArg: string): Record<string, string> => {
  const attrs: Record<string, string> = {};
  const regex = /([A-Za-z_:][-A-Za-z0-9_:.]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
  let match: RegExpExecArray | null;
  while ((match = regex.exec(valueArg))) {
    attrs[match[1]] = unescapeXml(match[2] ?? match[3] ?? '');
  }
  return attrs;
};

const stripTags = (valueArg: string): string => unescapeXml(valueArg.replace(/<[^>]+>/g, ''));

const numberValue = (valueArg: unknown): number | undefined => {
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
    return valueArg;
  }
  if (typeof valueArg === 'string' && valueArg.trim()) {
    const parsed = Number(valueArg);
    return Number.isFinite(parsed) ? parsed : undefined;
  }
  return undefined;
};

const booleanValue = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'string') {
    const normalized = valueArg.toLowerCase();
    if (normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on') {
      return true;
    }
    if (normalized === 'false' || normalized === '0' || normalized === 'no' || normalized === 'off') {
      return false;
    }
  }
  return undefined;
};

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

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

const escapeRegExp = (valueArg: string): string => valueArg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => {
  return Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg));
};

const isFetchResponse = (valueArg: unknown): valueArg is Response => {
  return Boolean(valueArg && typeof valueArg === 'object' && typeof (valueArg as Response).text === 'function');
};

const formatHost = (hostArg: string): string => hostArg.includes(':') && !hostArg.startsWith('[') ? `[${hostArg}]` : hostArg;
