import * as plugins from '../../plugins.js';
import { SimpleLocalClient, type IServiceCallRequest, type IServiceCallResult, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot } from '../../core/index.js';
import type { ILivisiConfig } from './livisi.types.js';
import { livisiDefaultPort, livisiDefaultTimeoutMs, livisiProfile } from './livisi.types.js';

const authHeader = 'Basic Y2xpZW50SWQ6Y2xpZW50UGFzcw==';
const switchDeviceTypes = new Set(['ISS', 'ISS2', 'PSS', 'PSSO']);
const windowDoorSensorType = 'WDS';
const virtualClimateControlType = 'VRCC';

export class LivisiApiError extends Error {
  constructor(messageArg: string, public readonly errorCode?: number) {
    super(messageArg);
    this.name = 'LivisiApiError';
  }
}

export class LivisiClient extends SimpleLocalClient<ILivisiConfig> {
  private currentLiveSnapshot?: ISimpleLocalSnapshot;
  private bearerToken?: string;

  constructor(configArg: ILivisiConfig) {
    super(livisiProfile, configArg);
    this.localConfig = configArg;
    this.bearerToken = configArg.token;
  }

  private readonly localConfig: ILivisiConfig;

  public override async getSnapshot(forceRefreshArg = false): Promise<ISimpleLocalSnapshot> {
    if (this.shouldUseSimpleLocal()) {
      return super.getSnapshot(forceRefreshArg);
    }

    if (!forceRefreshArg && this.currentLiveSnapshot) {
      return clone(this.currentLiveSnapshot);
    }

    if (!this.hasLiveTarget()) {
      const snapshot = this.httpOfflineSnapshot('LIVISI live HTTP polling requires config.host/ipAddress and config.password or token.');
      this.currentLiveSnapshot = snapshot;
      return clone(snapshot);
    }

    try {
      const snapshot = await this.fetchLiveSnapshot();
      this.currentLiveSnapshot = snapshot;
      return clone(snapshot);
    } catch (errorArg) {
      const snapshot = this.httpOfflineSnapshot(errorMessage(errorArg));
      this.currentLiveSnapshot = snapshot;
      return clone(snapshot);
    }
  }

  public override async refresh(): Promise<IServiceCallResult> {
    if (this.shouldUseSimpleLocal()) {
      return super.refresh();
    }
    this.currentLiveSnapshot = undefined;
    const snapshot = await this.getSnapshot(true);
    const success = snapshot.online && !snapshot.error;
    return { success, error: success ? undefined : snapshot.error, data: snapshot };
  }

  public override async execute(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (this.localConfig.client?.execute || this.localConfig.commandExecutor) {
      return super.execute(requestArg);
    }

    if (requestArg.service === 'refresh' || requestArg.service === 'reload') {
      return this.refresh();
    }

    if (!this.hasLiveTarget()) {
      return super.execute(requestArg);
    }

    if (requestArg.service === 'set_hvac_mode') {
      const hvacMode = stringValue(requestArg.data?.hvacMode) || stringValue(requestArg.data?.hvac_mode);
      if (!hvacMode || hvacMode === 'heat') {
        return { success: true, data: { hvacMode: 'heat', note: 'LIVISI VRCC exposes heat-only HVAC mode.' } };
      }
      return { success: false, error: `LIVISI VRCC supports heat mode only, not ${hvacMode}.` };
    }

    try {
      if (requestArg.service === 'turn_on' || requestArg.service === 'turn_off' || requestArg.service === 'toggle') {
        const capabilityId = await this.capabilityIdFromRequest(requestArg, 'SwitchActuator');
        if (!capabilityId) {
          return { success: false, error: 'LIVISI switch command requires data.capabilityId or a target matching a switch entity.' };
        }
        const value = requestArg.service === 'toggle' ? !(await this.switchStateForCapability(capabilityId)) : requestArg.service === 'turn_on';
        return this.sendCapabilitySetState(capabilityId, 'onState', value);
      }

      if (requestArg.service === 'set_temperature') {
        const capabilityId = await this.capabilityIdFromRequest(requestArg, 'RoomSetpoint');
        const temperature = numberValue(requestArg.data?.temperature);
        if (!capabilityId) {
          return { success: false, error: 'LIVISI set_temperature requires data.capabilityId or a target matching a climate entity.' };
        }
        if (temperature === undefined) {
          return { success: false, error: 'LIVISI set_temperature requires numeric data.temperature.' };
        }
        const snapshot = await this.getSnapshot();
        const key = snapshot.device.model === 'Avatar' ? 'setpointTemperature' : 'pointTemperature';
        return this.sendCapabilitySetState(capabilityId, key, temperature);
      }
    } catch (errorArg) {
      return { success: false, error: errorMessage(errorArg) };
    }

    return super.execute(requestArg);
  }

  private shouldUseSimpleLocal(): boolean {
    return Boolean(
      this.localConfig.snapshot
      || this.localConfig.rawData !== undefined
      || this.localConfig.rawText !== undefined
      || this.localConfig.entities?.length
      || this.localConfig.client
      || this.localConfig.snapshotProvider,
    );
  }

  private hasLiveTarget(): boolean {
    return Boolean(this.host() && (this.localConfig.password || this.localConfig.token || this.bearerToken));
  }

  private async fetchLiveSnapshot(): Promise<ISimpleLocalSnapshot> {
    const controller = controllerData(await this.authorizedRequest('get', 'status'));
    const [devicesRaw, capabilitiesRaw, locationsRaw] = await Promise.all([
      this.authorizedRequest('get', 'device'),
      this.authorizedRequest('get', 'capability'),
      this.authorizedRequest('get', 'location'),
    ]);
    const devices = arrayRecords(devicesRaw);
    const capabilities = arrayRecords(capabilitiesRaw);
    const locations = arrayRecords(locationsRaw);
    const states = await this.fetchCapabilityStates(capabilities);
    return this.snapshotFromLiveData(controller, devices, capabilities, locations, states);
  }

  private async fetchCapabilityStates(capabilitiesArg: Array<Record<string, unknown>>): Promise<Record<string, unknown>> {
    const entries = await Promise.all(capabilitiesArg.map(async (capabilityArg) => {
      const id = normalizeCapabilityId(stringValue(capabilityArg.id));
      if (!id || !this.shouldReadCapabilityState(stringValue(capabilityArg.type))) {
        return undefined;
      }
      try {
        return [id, await this.authorizedRequest('get', `capability/${id}/state`)] as const;
      } catch (errorArg) {
        return [id, { error: errorMessage(errorArg) }] as const;
      }
    }));
    return Object.fromEntries(entries.filter((entryArg): entryArg is readonly [string, unknown] => Boolean(entryArg)));
  }

  private shouldReadCapabilityState(typeArg: string | undefined): boolean {
    return Boolean(typeArg && ['SwitchActuator', 'WindowDoorSensor', 'RoomSetpoint', 'RoomTemperature', 'RoomHumidity'].includes(typeArg));
  }

  private snapshotFromLiveData(
    controllerArg: Record<string, unknown>,
    devicesArg: Array<Record<string, unknown>>,
    capabilitiesArg: Array<Record<string, unknown>>,
    locationsArg: Array<Record<string, unknown>>,
    statesArg: Record<string, unknown>,
  ): ISimpleLocalSnapshot {
    const capabilityMaps = this.capabilityMaps(capabilitiesArg);
    const roomMap = this.roomMap(locationsArg);
    const controllerType = stringValue(controllerArg.controllerType) || 'SHC';
    const serialNumber = stringValue(controllerArg.serialNumber);
    const host = this.host();
    const port = this.port();
    const entities: ISimpleLocalEntitySnapshot[] = [];

    for (const device of devicesArg) {
      const deviceType = stringValue(device.type);
      const deviceId = stringValue(device.id);
      if (!deviceId || !deviceType) {
        continue;
      }
      const capabilities = capabilityMaps.byDevice[deviceId] || {};
      if (deviceType === windowDoorSensorType && capabilities.WindowDoorSensor) {
        entities.push(this.windowDoorEntity(device, roomMap, capabilities.WindowDoorSensor, statesArg[capabilities.WindowDoorSensor]));
      } else if (switchDeviceTypes.has(deviceType) && capabilities.SwitchActuator) {
        entities.push(this.switchEntity(device, roomMap, capabilities.SwitchActuator, statesArg[capabilities.SwitchActuator]));
      } else if (deviceType === virtualClimateControlType && capabilities.RoomSetpoint) {
        entities.push(this.climateEntity(device, roomMap, capabilities, capabilityMaps.configByDevice[deviceId] || {}, statesArg, controllerType));
      }
    }

    return {
      device: {
        id: this.localConfig.uniqueId || serialNumber || (host ? `${host}:${port}` : undefined) || livisiProfile.domain,
        name: this.localConfig.name || `SHC ${controllerType}${serialNumber ? ` ${serialNumber}` : ''}`,
        manufacturer: 'Livisi',
        model: controllerType,
        serialNumber,
        host,
        port,
        protocol: 'http',
        configurationUrl: host ? `http://${host}:${port}` : undefined,
        attributes: cleanAttributes({ ...controllerArg }),
      },
      entities,
      online: true,
      updatedAt: new Date().toISOString(),
      source: 'http',
      rawData: {
        controller: controllerArg,
        devices: devicesArg,
        capabilities: capabilitiesArg,
        locations: locationsArg,
        states: statesArg,
      },
      capabilities: {
        localRead: true,
        localControl: true,
        httpPolling: true,
      },
    };
  }

  private windowDoorEntity(deviceArg: Record<string, unknown>, roomMapArg: Record<string, string>, capabilityIdArg: string, stateArg: unknown): ISimpleLocalEntitySnapshot {
    const isOpen = stateValue(stateArg, 'isOpen');
    const tags = recordValue(deviceArg.tags);
    return {
      id: `${slug(stringValue(deviceArg.id))}_window_door`,
      uniqueId: `${livisiProfile.domain}_${slug(stringValue(deviceArg.id))}_window_door`,
      name: this.deviceName(deviceArg),
      platform: 'binary_sensor',
      state: booleanValue(isOpen) ?? false,
      available: isOpen !== undefined && !booleanValue(deviceArg.unreachable),
      deviceClass: stringValue(tags.typeCategory) === 'TCDoorId' ? 'door' : 'window',
      attributes: this.entityAttributes(deviceArg, roomMapArg, capabilityIdArg, 'WindowDoorSensor'),
    };
  }

  private switchEntity(deviceArg: Record<string, unknown>, roomMapArg: Record<string, string>, capabilityIdArg: string, stateArg: unknown): ISimpleLocalEntitySnapshot {
    const onState = stateValue(stateArg, 'onState');
    return {
      id: `${slug(stringValue(deviceArg.id))}_switch`,
      uniqueId: `${livisiProfile.domain}_${slug(stringValue(deviceArg.id))}_switch`,
      name: this.deviceName(deviceArg),
      platform: 'switch',
      state: booleanValue(onState) ?? false,
      available: onState !== undefined && !booleanValue(deviceArg.unreachable),
      writable: true,
      attributes: this.entityAttributes(deviceArg, roomMapArg, capabilityIdArg, 'SwitchActuator'),
    };
  }

  private climateEntity(
    deviceArg: Record<string, unknown>,
    roomMapArg: Record<string, string>,
    capabilitiesArg: Record<string, string>,
    capabilityConfigArg: Record<string, Record<string, unknown>>,
    statesArg: Record<string, unknown>,
    controllerTypeArg: string,
  ): ISimpleLocalEntitySnapshot {
    const targetCapability = capabilitiesArg.RoomSetpoint;
    const temperatureCapability = capabilitiesArg.RoomTemperature;
    const humidityCapability = capabilitiesArg.RoomHumidity;
    const targetKey = controllerTypeArg === 'Avatar' ? 'setpointTemperature' : 'pointTemperature';
    const targetTemperature = stateValue(statesArg[targetCapability], targetKey);
    const currentTemperature = stateValue(statesArg[temperatureCapability], 'temperature');
    const humidity = stateValue(statesArg[humidityCapability], 'humidity');
    const setpointConfig = capabilityConfigArg.RoomSetpoint || {};
    return {
      id: `${slug(stringValue(deviceArg.id))}_climate`,
      uniqueId: `${livisiProfile.domain}_${slug(stringValue(deviceArg.id))}_climate`,
      name: this.deviceName(deviceArg),
      platform: 'climate',
      state: {
        hvacMode: 'heat',
        targetTemperature,
        currentTemperature,
        currentHumidity: humidity,
      },
      available: currentTemperature !== undefined && !booleanValue(deviceArg.unreachable),
      writable: true,
      unit: '°C',
      attributes: cleanAttributes({
        ...this.entityAttributes(deviceArg, roomMapArg, targetCapability, 'RoomSetpoint'),
        temperatureCapability,
        humidityCapability,
        minTemperature: numberValue(setpointConfig.minTemperature) ?? 6,
        maxTemperature: numberValue(setpointConfig.maxTemperature) ?? 30,
        supportedHvacModes: ['heat'],
      }),
    };
  }

  private entityAttributes(deviceArg: Record<string, unknown>, roomMapArg: Record<string, string>, capabilityIdArg: string | undefined, capabilityTypeArg: string): Record<string, unknown> | undefined {
    const deviceId = stringValue(deviceArg.id);
    return cleanAttributes({
      deviceId,
      deviceType: stringValue(deviceArg.type),
      capabilityId: capabilityIdArg,
      capabilityType: capabilityTypeArg,
      manufacturer: stringValue(deviceArg.manufacturer),
      room: this.deviceRoom(deviceArg, roomMapArg),
      batteryLow: booleanValue(deviceArg.battery_low),
      updateAvailable: booleanValue(deviceArg.update_available),
      unreachable: booleanValue(deviceArg.unreachable),
    });
  }

  private deviceName(deviceArg: Record<string, unknown>): string {
    const config = recordValue(deviceArg.config);
    return stringValue(config.name) || stringValue(deviceArg.name) || stringValue(deviceArg.id) || livisiProfile.defaultName || livisiProfile.displayName;
  }

  private deviceRoom(deviceArg: Record<string, unknown>, roomMapArg: Record<string, string>): string | undefined {
    const location = normalizeLocationId(stringValue(deviceArg.location));
    return location ? roomMapArg[location] : undefined;
  }

  private capabilityMaps(capabilitiesArg: Array<Record<string, unknown>>): {
    byDevice: Record<string, Record<string, string>>;
    configByDevice: Record<string, Record<string, Record<string, unknown>>>;
  } {
    const byDevice: Record<string, Record<string, string>> = {};
    const configByDevice: Record<string, Record<string, Record<string, unknown>>> = {};
    for (const capability of capabilitiesArg) {
      const deviceId = normalizeDeviceId(stringValue(capability.device));
      const type = stringValue(capability.type);
      const id = normalizeCapabilityId(stringValue(capability.id));
      if (!deviceId || !type || !id) {
        continue;
      }
      byDevice[deviceId] ||= {};
      byDevice[deviceId][type] = id;
      const config = recordValue(capability.config);
      if (Object.keys(config).length) {
        configByDevice[deviceId] ||= {};
        configByDevice[deviceId][type] = config;
      }
    }
    return { byDevice, configByDevice };
  }

  private roomMap(locationsArg: Array<Record<string, unknown>>): Record<string, string> {
    const result: Record<string, string> = {};
    for (const location of locationsArg) {
      const id = normalizeLocationId(stringValue(location.id));
      const config = recordValue(location.config);
      const name = stringValue(config.name);
      if (id && name) {
        result[id] = name;
      }
    }
    return result;
  }

  private async capabilityIdFromRequest(requestArg: IServiceCallRequest, preferredTypeArg?: string): Promise<string | undefined> {
    const direct = normalizeCapabilityId(
      stringValue(requestArg.data?.capabilityId)
      || stringValue(requestArg.data?.capability_id)
      || stringValue(requestArg.data?.target)
    );
    if (direct) {
      return direct;
    }

    const target = requestArg.target.entityId || requestArg.target.deviceId || stringValue(requestArg.data?.entityId) || stringValue(requestArg.data?.deviceId);
    if (!target) {
      return undefined;
    }

    const snapshot = await this.getSnapshot();
    const entity = snapshot.entities.find((entityArg) => {
      const attributes = entityArg.attributes || {};
      return (
        entityArg.id === target
        || entityArg.uniqueId === target
        || attributes.deviceId === target
        || attributes.capabilityId === target
        || (typeof target === 'string' && target.endsWith(`.${slug(`${snapshot.device.name}_${entityArg.id}`)}`))
      ) && (!preferredTypeArg || attributes.capabilityType === preferredTypeArg);
    });
    return normalizeCapabilityId(stringValue(entity?.attributes?.capabilityId));
  }

  private async switchStateForCapability(capabilityIdArg: string): Promise<boolean> {
    const snapshot = await this.getSnapshot(true);
    const entity = snapshot.entities.find((entityArg) => entityArg.attributes?.capabilityId === capabilityIdArg);
    return booleanValue(entity?.state) ?? false;
  }

  private async sendCapabilitySetState(capabilityIdArg: string, keyArg: string, valueArg: boolean | number): Promise<IServiceCallResult> {
    const response = recordValue(await this.authorizedRequest('post', 'action', {
      id: plugins.crypto.randomUUID().replace(/-/g, ''),
      type: 'SetState',
      namespace: 'core.RWE',
      target: `/capability/${capabilityIdArg}`,
      params: { [keyArg]: { type: 'Constant', value: valueArg } },
    }));
    const success = response.resultCode === 'Success';
    return {
      success,
      error: success ? undefined : `LIVISI action failed with resultCode ${String(response.resultCode || 'missing')}.`,
      data: response,
    };
  }

  private async authorizedRequest(methodArg: 'get' | 'post', pathArg: string, payloadArg?: unknown): Promise<unknown> {
    await this.ensureToken();
    try {
      return await this.requestJson(methodArg, pathArg, payloadArg, true);
    } catch (errorArg) {
      if (errorArg instanceof LivisiApiError && errorArg.errorCode === 2007 && this.localConfig.password) {
        this.bearerToken = undefined;
        await this.ensureToken();
        return this.requestJson(methodArg, pathArg, payloadArg, true);
      }
      throw errorArg;
    }
  }

  private async ensureToken(): Promise<void> {
    if (this.bearerToken) {
      return;
    }
    if (!this.localConfig.password) {
      throw new LivisiApiError('LIVISI authorization requires config.password or token.');
    }
    const response = recordValue(await this.requestJson('post', 'auth/token', {
      username: 'admin',
      password: this.localConfig.password,
      grant_type: 'password',
    }, false));
    const token = stringValue(response.access_token);
    if (!token) {
      const errorCode = numberValue(response.errorcode);
      if (errorCode === 2009 || errorCode === 2003) {
        throw new LivisiApiError('LIVISI authorization failed: wrong admin password.', errorCode);
      }
      throw new LivisiApiError(`LIVISI authorization did not return an access_token${errorCode ? ` (errorcode ${errorCode})` : ''}.`, errorCode);
    }
    this.bearerToken = token;
  }

  private async requestJson(methodArg: 'get' | 'post', pathArg: string, payloadArg: unknown, authorizedArg: boolean): Promise<unknown> {
    const host = this.host();
    if (!host) {
      throw new LivisiApiError('LIVISI HTTP request requires config.host or ipAddress.');
    }
    const url = `http://${hostForUrl(host)}:${this.port()}/${pathArg.replace(/^\/+/, '')}`;
    const headers: Record<string, string> = {
      accept: authorizedArg ? '*/*' : 'application/json',
      'content-type': 'application/json',
    };
    if (authorizedArg) {
      headers.authorization = `Bearer ${this.bearerToken}`;
    } else {
      headers.authorization = authHeader;
    }

    let response: Response;
    try {
      response = await globalThis.fetch(url, {
        method: methodArg.toUpperCase(),
        headers,
        body: payloadArg === undefined ? undefined : JSON.stringify(payloadArg),
        signal: AbortSignal.timeout(this.localConfig.timeoutMs || livisiDefaultTimeoutMs),
      });
    } catch (errorArg) {
      throw new LivisiApiError(`LIVISI HTTP ${methodArg.toUpperCase()} ${url} failed: ${errorMessage(errorArg)}`);
    }

    const text = await response.text();
    const data = parseMaybeJson(text);
    if (!response.ok) {
      throw new LivisiApiError(`LIVISI HTTP ${methodArg.toUpperCase()} ${url} failed with ${response.status}: ${text.slice(0, 160)}`, response.status === 401 ? 2009 : undefined);
    }
    const errorCode = data && typeof data === 'object' && !Array.isArray(data) ? numberValue((data as Record<string, unknown>).errorcode) : undefined;
    if (errorCode !== undefined) {
      throw new LivisiApiError(`LIVISI SHC returned errorcode ${errorCode}.`, errorCode);
    }
    return data;
  }

  private host(): string | undefined {
    if (this.localConfig.ipAddress) {
      return this.localConfig.ipAddress;
    }
    if (this.localConfig.host) {
      return this.localConfig.host;
    }
    if (this.localConfig.url) {
      try {
        return new URL(this.localConfig.url).hostname;
      } catch {}
    }
    return undefined;
  }

  private port(): number {
    if (this.localConfig.port) {
      return this.localConfig.port;
    }
    if (this.localConfig.url) {
      try {
        const parsed = new URL(this.localConfig.url);
        if (parsed.port) {
          return Number(parsed.port);
        }
      } catch {}
    }
    return livisiDefaultPort;
  }

  private httpOfflineSnapshot(errorArg: string): ISimpleLocalSnapshot {
    const host = this.host();
    const port = host ? this.port() : undefined;
    return {
      device: {
        id: this.localConfig.uniqueId || (host ? `${host}:${port}` : undefined) || this.localConfig.name || livisiProfile.domain,
        name: this.localConfig.name || livisiProfile.defaultName || livisiProfile.displayName,
        manufacturer: 'Livisi',
        model: livisiProfile.model,
        host,
        port,
        protocol: 'http',
        configurationUrl: host ? `http://${host}:${port}` : undefined,
      },
      entities: [],
      online: false,
      updatedAt: new Date().toISOString(),
      source: 'runtime',
      error: errorArg,
      capabilities: {
        localRead: true,
        localControl: true,
        httpPolling: true,
      },
    };
  }
}

const controllerData = (valueArg: unknown): Record<string, unknown> => {
  const record = recordValue(valueArg);
  const gateway = recordValue(record.gateway);
  return Object.keys(gateway).length ? gateway : record;
};

const stateValue = (stateArg: unknown, keyArg: string): unknown => {
  const state = recordValue(stateArg);
  const value = state[keyArg];
  if (value && typeof value === 'object' && !Array.isArray(value) && 'value' in value) {
    return (value as Record<string, unknown>).value;
  }
  return value;
};

const recordValue = (valueArg: unknown): Record<string, unknown> => valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : {};

const arrayRecords = (valueArg: unknown): Array<Record<string, unknown>> => Array.isArray(valueArg) ? valueArg.filter((itemArg): itemArg is Record<string, unknown> => Boolean(itemArg && typeof itemArg === 'object' && !Array.isArray(itemArg))) : [];

const stringValue = (valueArg: unknown): string | undefined => typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;

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

const booleanValue = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'string') {
    if (valueArg.toLowerCase() === 'true') {
      return true;
    }
    if (valueArg.toLowerCase() === 'false') {
      return false;
    }
  }
  return undefined;
};

const cleanAttributes = (attributesArg: Record<string, unknown>): Record<string, unknown> | undefined => {
  const clean = Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  return Object.keys(clean).length ? clean : undefined;
};

const normalizeDeviceId = (valueArg: string | undefined): string | undefined => valueArg?.replace(/^\/device\//, '');

const normalizeCapabilityId = (valueArg: string | undefined): string | undefined => valueArg?.replace(/^\/capability\//, '');

const normalizeLocationId = (valueArg: string | undefined): string | undefined => valueArg?.replace(/^\/location\//, '');

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

const slug = (valueArg: unknown): string => String(valueArg || '').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'device';

const parseMaybeJson = (textArg: string): unknown => {
  const trimmed = textArg.trim();
  if (!trimmed) {
    return {};
  }
  try {
    return JSON.parse(trimmed) as unknown;
  } catch {
    return trimmed;
  }
};

const clone = <TValue>(valueArg: TValue): TValue => JSON.parse(JSON.stringify(valueArg)) as TValue;

const errorMessage = (errorArg: unknown): string => errorArg instanceof Error ? errorArg.message : String(errorArg);
