import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type { IKnxClientCommand, IKnxConfig, IKnxEntityDescriptor, IKnxEvent, IKnxGatewayDescriptor, IKnxGroupAddressDescriptor, IKnxSnapshot, IKnxTelegramCommand, TKnxEntityPlatform } from './knx.types.js';

const defaultPort = 3671;
const defaultMulticastGroup = '224.0.23.12';
const defaultRoutingAddress = '0.0.240';
const mappedPlatforms = ['light', 'switch', 'sensor', 'cover', 'climate'] as const;

export class KnxMapper {
  public static toSnapshot(configArg: IKnxConfig, connectedArg?: boolean, eventsArg: IKnxEvent[] = []): IKnxSnapshot {
    const source = configArg.snapshot;
    const gateways = [
      ...(source?.gateways || []),
      ...(configArg.gateways || []),
      ...(configArg.tunnels || []),
      ...(configArg.gateway ? [configArg.gateway] : []),
    ];
    const groupAddresses = [
      ...(source?.groupAddresses || []),
      ...(configArg.groupAddresses || []),
      ...(configArg.group_addresses || []),
    ];

    return {
      connected: connectedArg ?? source?.connected ?? false,
      connection: this.connectionSnapshot(configArg, source),
      gateways,
      groupAddresses,
      entities: [
        ...(source?.entities || []),
        ...(configArg.entities || []),
        ...this.entitiesFromPlatformConfig(configArg),
        ...this.entitiesFromGroupAddresses(groupAddresses),
      ],
      events: [...(source?.events || []), ...(configArg.events || []), ...eventsArg],
    };
  }

  public static toDevices(snapshotArg: IKnxSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = new Date().toISOString();
    const devices = new Map<string, plugins.shxInterfaces.data.IDeviceDefinition>();
    const interfaceId = this.interfaceDeviceId(snapshotArg);
    devices.set(interfaceId, {
      id: interfaceId,
      integrationDomain: 'knx',
      name: this.interfaceName(snapshotArg),
      protocol: 'unknown',
      manufacturer: 'KNX Association',
      model: this.connectionLabel(snapshotArg.connection.connectionType),
      online: snapshotArg.connected || Boolean(snapshotArg.entities.length || snapshotArg.gateways.length),
      features: [
        { id: 'connection', capability: 'sensor', name: 'Connection', readable: true, writable: false },
        { id: 'entity_count', capability: 'sensor', name: 'Entity count', readable: true, writable: false },
        { id: 'gateway_count', capability: 'sensor', name: 'Gateway count', readable: true, writable: false },
      ],
      state: [
        { featureId: 'connection', value: snapshotArg.connected ? 'connected' : 'configured', updatedAt },
        { featureId: 'entity_count', value: snapshotArg.entities.length, updatedAt },
        { featureId: 'gateway_count', value: snapshotArg.gateways.length, updatedAt },
      ],
      metadata: {
        protocol: 'knx',
        connection: snapshotArg.connection,
        gateways: snapshotArg.gateways,
      },
    });

    for (const entity of this.allEntities(snapshotArg)) {
      const platform = this.corePlatform(entity.platform || 'sensor');
      const deviceId = this.entityDeviceId(entity);
      const feature = this.featureForEntity(entity, platform);
      const state = { featureId: feature.id, value: this.deviceStateValue(this.entityState(entity, platform)), updatedAt };
      const existing = devices.get(deviceId);
      if (existing) {
        existing.features.push(feature);
        existing.state.push(state);
        continue;
      }
      devices.set(deviceId, {
        id: deviceId,
        integrationDomain: 'knx',
        name: this.deviceName(entity, platform),
        protocol: 'unknown',
        manufacturer: 'KNX',
        model: `KNX ${platform}`,
        online: entity.available !== false,
        features: [feature],
        state: [state],
        metadata: {
          protocol: 'knx',
          addresses: this.entityAddresses(entity),
          dpt: entity.dpt || entity.type,
          deviceClass: entity.deviceClass || entity.device_class,
          syncState: entity.syncState ?? entity.sync_state,
          raw: entity.raw,
        },
      });
    }

    return [...devices.values()];
  }

  public static toEntities(snapshotArg: IKnxSnapshot): IIntegrationEntity[] {
    return this.allEntities(snapshotArg).map((entityArg) => {
      const platform = this.corePlatform(entityArg.platform || 'sensor');
      return {
        id: entityArg.entityId || entityArg.entity_id || `${platform}.${this.slug(this.entityName(entityArg, platform))}`,
        uniqueId: this.uniqueIdForEntity(entityArg, platform),
        integrationDomain: 'knx',
        deviceId: this.entityDeviceId(entityArg),
        platform,
        name: this.entityName(entityArg, platform),
        state: this.entityState(entityArg, platform),
        attributes: {
          ...entityArg.attributes,
          knxPlatform: entityArg.platform,
          addresses: this.entityAddresses(entityArg),
          dpt: entityArg.dpt || entityArg.type,
          deviceClass: entityArg.deviceClass || entityArg.device_class,
          unit: entityArg.unit || entityArg.unitOfMeasurement || entityArg.unit_of_measurement,
          writable: this.entityWritable(entityArg, platform),
        },
        available: entityArg.available !== false,
      };
    });
  }

  public static commandForService(snapshotArg: IKnxSnapshot, requestArg: IServiceCallRequest): IKnxClientCommand | undefined {
    const entity = this.findTargetEntity(snapshotArg, requestArg);
    if (!entity) {
      return undefined;
    }
    const platform = this.corePlatform(entity.platform || requestArg.domain || 'sensor');
    const telegrams = this.telegramsForEntityService(entity, platform, requestArg);
    if (!telegrams.length) {
      return undefined;
    }
    return {
      type: 'entity.command',
      service: requestArg.service,
      platform,
      entityId: entity.entityId || entity.entity_id,
      uniqueId: this.uniqueIdForEntity(entity, platform),
      telegrams,
      payload: {
        serviceData: requestArg.data || {},
        entityName: this.entityName(entity, platform),
      },
      target: requestArg.target,
    };
  }

  public static groupWriteCommand(addressesArg: string[], payloadArg: unknown, dptArg?: string, responseArg = false): IKnxClientCommand | undefined {
    const telegrams = this.uniqueAddresses(addressesArg).map((addressArg) => ({
      action: responseArg ? 'response' as const : 'write' as const,
      address: addressArg,
      payload: payloadArg,
      dpt: dptArg,
    }));
    if (!telegrams.length) {
      return undefined;
    }
    return {
      type: 'group.write',
      service: 'send',
      telegrams,
      payload: {
        payload: payloadArg,
        dpt: dptArg,
        response: responseArg,
      },
    };
  }

  public static groupReadCommand(addressesArg: string[]): IKnxClientCommand | undefined {
    const telegrams = this.uniqueAddresses(addressesArg).map((addressArg) => ({
      action: 'read' as const,
      address: addressArg,
    }));
    if (!telegrams.length) {
      return undefined;
    }
    return {
      type: 'group.read',
      service: 'read',
      telegrams,
      payload: {},
    };
  }

  public static toIntegrationEvent(eventArg: IKnxEvent) {
    return {
      type: eventArg.type === 'device_removed' ? 'device_removed' as const : eventArg.type === 'availability_changed' ? 'availability_changed' as const : 'state_changed' as const,
      integrationDomain: 'knx',
      deviceId: eventArg.deviceId,
      entityId: eventArg.entityId,
      data: eventArg,
      timestamp: eventArg.timestamp || Date.now(),
    };
  }

  public static corePlatform(platformArg: TKnxEntityPlatform): TEntityPlatform {
    const platform = String(platformArg).toLowerCase();
    if (platform === 'binary_sensor') {
      return 'sensor';
    }
    const supported: TEntityPlatform[] = ['light', 'switch', 'sensor', 'cover', 'climate'];
    return supported.includes(platform as TEntityPlatform) ? platform as TEntityPlatform : 'sensor';
  }

  private static connectionSnapshot(configArg: IKnxConfig, sourceArg?: IKnxSnapshot) {
    const source = sourceArg?.connection;
    const connectionType = this.stringValue(configArg.connectionType || configArg.connection_type || source?.connectionType)
      || (configArg.host || source?.host ? 'tunneling' : 'automatic');
    const port = this.numberValue(configArg.port ?? source?.port) || defaultPort;
    const multicastPort = this.numberValue(configArg.multicastPort ?? configArg.multicast_port ?? source?.multicastPort ?? source?.multicast_port) || defaultPort;
    return {
      ...source,
      connectionType,
      host: this.stringValue(configArg.host || source?.host),
      port,
      individualAddress: this.stringValue(configArg.individualAddress || configArg.individual_address || source?.individualAddress || source?.individual_address) || defaultRoutingAddress,
      localIp: this.stringValue(configArg.localIp || configArg.local_ip || source?.localIp || source?.local_ip),
      multicastGroup: this.stringValue(configArg.multicastGroup || configArg.multicast_group || source?.multicastGroup || source?.multicast_group) || defaultMulticastGroup,
      multicastPort,
      routeBack: Boolean(configArg.routeBack ?? configArg.route_back ?? source?.routeBack ?? source?.route_back),
      tunnelEndpointIa: this.stringValue(configArg.tunnelEndpointIa || configArg.tunnel_endpoint_ia || source?.tunnelEndpointIa || source?.tunnel_endpoint_ia),
      stateUpdater: configArg.stateUpdater ?? configArg.state_updater ?? source?.stateUpdater ?? source?.state_updater ?? true,
      rateLimit: this.numberValue(configArg.rateLimit ?? configArg.rate_limit ?? source?.rateLimit ?? source?.rate_limit) || 0,
      telegramLogSize: this.numberValue(configArg.telegramLogSize ?? configArg.telegram_log_size ?? source?.telegramLogSize ?? source?.telegram_log_size),
      secure: connectionType.includes('secure') || Boolean(configArg.userId ?? configArg.user_id ?? configArg.backboneKey ?? configArg.backbone_key),
    };
  }

  private static entitiesFromPlatformConfig(configArg: IKnxConfig): IKnxEntityDescriptor[] {
    const entities: IKnxEntityDescriptor[] = [];
    for (const platform of mappedPlatforms) {
      for (const item of this.asArray(configArg[platform])) {
        const entity = this.asRecord(item);
        if (entity) {
          entities.push({ ...entity, platform } as IKnxEntityDescriptor);
        }
      }
    }
    return entities;
  }

  private static entitiesFromGroupAddresses(groupAddressesArg: IKnxGroupAddressDescriptor[]): IKnxEntityDescriptor[] {
    const entities: IKnxEntityDescriptor[] = [];
    for (const groupAddress of groupAddressesArg) {
      const platform = groupAddress.platform ? this.corePlatform(groupAddress.platform) : undefined;
      if (!platform) {
        continue;
      }
      const role = String(groupAddress.role || '').toLowerCase();
      entities.push({
        platform,
        entityId: groupAddress.entityId || groupAddress.entity_id,
        uniqueId: groupAddress.uniqueId || groupAddress.unique_id,
        deviceId: groupAddress.deviceId || groupAddress.device_id,
        name: groupAddress.name,
        address: role.includes('state') || role.includes('status') ? undefined : groupAddress.address,
        stateAddress: role.includes('state') || role.includes('status') ? groupAddress.address : undefined,
        dpt: groupAddress.dpt || groupAddress.type,
        state: groupAddress.state ?? groupAddress.value,
        writable: groupAddress.writable,
        unit: groupAddress.unit,
        attributes: groupAddress.metadata,
      });
    }
    return entities;
  }

  private static allEntities(snapshotArg: IKnxSnapshot): IKnxEntityDescriptor[] {
    return snapshotArg.entities.filter((entityArg) => mappedPlatforms.includes(this.corePlatform(entityArg.platform || 'sensor') as typeof mappedPlatforms[number]));
  }

  private static findTargetEntity(snapshotArg: IKnxSnapshot, requestArg: IServiceCallRequest): IKnxEntityDescriptor | undefined {
    const entities = this.allEntities(snapshotArg);
    if (requestArg.target.entityId) {
      const normalized = this.toEntities(snapshotArg).find((entityArg) => entityArg.id === requestArg.target.entityId || entityArg.uniqueId === requestArg.target.entityId);
      if (normalized) {
        return entities.find((entityArg) => this.uniqueIdForEntity(entityArg, this.corePlatform(entityArg.platform || 'sensor')) === normalized.uniqueId);
      }
      return entities.find((entityArg) => entityArg.entityId === requestArg.target.entityId || entityArg.entity_id === requestArg.target.entityId || entityArg.uniqueId === requestArg.target.entityId || entityArg.unique_id === requestArg.target.entityId);
    }
    if (requestArg.target.deviceId) {
      return entities.find((entityArg) => this.entityDeviceId(entityArg) === requestArg.target.deviceId && this.entityWritable(entityArg, this.corePlatform(entityArg.platform || 'sensor')))
        || entities.find((entityArg) => this.entityDeviceId(entityArg) === requestArg.target.deviceId);
    }
    return entities.find((entityArg) => this.corePlatform(entityArg.platform || 'sensor') === requestArg.domain && this.entityWritable(entityArg, this.corePlatform(entityArg.platform || 'sensor')))
      || entities.find((entityArg) => this.entityWritable(entityArg, this.corePlatform(entityArg.platform || 'sensor')));
  }

  private static telegramsForEntityService(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform, requestArg: IServiceCallRequest): IKnxTelegramCommand[] {
    if (platformArg === 'light' || platformArg === 'switch') {
      return this.switchTelegrams(entityArg, platformArg, requestArg);
    }
    if (platformArg === 'cover') {
      return this.coverTelegrams(entityArg, requestArg);
    }
    if (platformArg === 'climate') {
      return this.climateTelegrams(entityArg, requestArg);
    }
    return [];
  }

  private static switchTelegrams(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform, requestArg: IServiceCallRequest): IKnxTelegramCommand[] {
    const switchAddresses = this.addressesFrom(entityArg, ['address', 'groupAddress', 'group_address', 'switchAddress', 'switch_address']);
    const brightnessAddresses = this.addressesFrom(entityArg, ['brightnessAddress', 'brightness_address']);
    if (requestArg.service === 'turn_on') {
      if (platformArg === 'light' && typeof requestArg.data?.brightness === 'number' && brightnessAddresses.length) {
        return this.writeTelegrams(brightnessAddresses, requestArg.data.brightness, entityArg.dpt || '5.001');
      }
      return this.writeTelegrams(switchAddresses, this.booleanPayload(true, entityArg.invert === true), entityArg.dpt || '1.001');
    }
    if (requestArg.service === 'turn_off') {
      if (!switchAddresses.length && platformArg === 'light' && brightnessAddresses.length) {
        return this.writeTelegrams(brightnessAddresses, 0, entityArg.dpt || '5.001');
      }
      return this.writeTelegrams(switchAddresses, this.booleanPayload(false, entityArg.invert === true), entityArg.dpt || '1.001');
    }
    return [];
  }

  private static coverTelegrams(entityArg: IKnxEntityDescriptor, requestArg: IServiceCallRequest): IKnxTelegramCommand[] {
    const longAddresses = this.addressesFrom(entityArg, ['moveLongAddress', 'move_long_address', 'address', 'groupAddress', 'group_address']);
    const stopAddresses = this.addressesFrom(entityArg, ['stopAddress', 'stop_address']);
    const positionAddresses = this.addressesFrom(entityArg, ['positionAddress', 'position_address']);
    const invertUpdown = entityArg.invertUpdown === true || entityArg.invert_updown === true;
    const invertPosition = entityArg.invertPosition === true || entityArg.invert_position === true;
    if (requestArg.service === 'open_cover') {
      return this.writeTelegrams(longAddresses, this.booleanPayload(false, invertUpdown), '1.001');
    }
    if (requestArg.service === 'close_cover') {
      return this.writeTelegrams(longAddresses, this.booleanPayload(true, invertUpdown), '1.001');
    }
    if (requestArg.service === 'stop_cover') {
      return this.writeTelegrams(stopAddresses, true, '1.001');
    }
    if (requestArg.service === 'set_cover_position' || requestArg.service === 'set_position') {
      const position = this.numberValue(requestArg.data?.position ?? requestArg.data?.percentage);
      if (position === undefined) {
        return [];
      }
      const knxPosition = invertPosition ? position : 100 - position;
      return this.writeTelegrams(positionAddresses, knxPosition, '5.001');
    }
    return [];
  }

  private static climateTelegrams(entityArg: IKnxEntityDescriptor, requestArg: IServiceCallRequest): IKnxTelegramCommand[] {
    const targetTemperatureAddresses = this.addressesFrom(entityArg, ['targetTemperatureAddress', 'target_temperature_address']);
    const onOffAddresses = this.addressesFrom(entityArg, ['onOffAddress', 'on_off_address']);
    const modeAddresses = this.addressesFrom(entityArg, ['controllerModeAddress', 'controller_mode_address', 'operationModeAddress', 'operation_mode_address']);
    if (requestArg.service === 'set_temperature') {
      const temperature = this.numberValue(requestArg.data?.temperature);
      return temperature === undefined ? [] : this.writeTelegrams(targetTemperatureAddresses, temperature, '9.001');
    }
    if (requestArg.service === 'turn_on') {
      return this.writeTelegrams(onOffAddresses, this.booleanPayload(true, entityArg.invert === true), '1.001');
    }
    if (requestArg.service === 'turn_off') {
      if (onOffAddresses.length) {
        return this.writeTelegrams(onOffAddresses, this.booleanPayload(false, entityArg.invert === true), '1.001');
      }
      return this.writeTelegrams(modeAddresses, 'off', '20.105');
    }
    if (requestArg.service === 'set_hvac_mode') {
      const mode = this.stringValue(requestArg.data?.hvac_mode ?? requestArg.data?.hvacMode);
      return mode ? this.writeTelegrams(modeAddresses, mode, '20.105') : [];
    }
    return [];
  }

  private static writeTelegrams(addressesArg: string[], payloadArg: unknown, dptArg?: string): IKnxTelegramCommand[] {
    return this.uniqueAddresses(addressesArg).map((addressArg) => ({ action: 'write' as const, address: addressArg, payload: payloadArg, dpt: dptArg }));
  }

  private static featureForEntity(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform): plugins.shxInterfaces.data.IDeviceFeature {
    return {
      id: this.slug(entityArg.uniqueId || entityArg.unique_id || entityArg.entityId || entityArg.entity_id || this.entityName(entityArg, platformArg)),
      capability: this.capabilityForPlatform(platformArg),
      name: this.entityName(entityArg, platformArg),
      readable: true,
      writable: this.entityWritable(entityArg, platformArg),
      unit: entityArg.unit || entityArg.unitOfMeasurement || entityArg.unit_of_measurement,
    };
  }

  private static entityWritable(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform): boolean {
    if (typeof entityArg.writable === 'boolean') {
      return entityArg.writable;
    }
    return ['light', 'switch', 'cover', 'climate'].includes(platformArg) && this.addressesFrom(entityArg, ['address', 'groupAddress', 'group_address', 'switchAddress', 'switch_address', 'moveLongAddress', 'move_long_address', 'positionAddress', 'position_address', 'targetTemperatureAddress', 'target_temperature_address', 'onOffAddress', 'on_off_address']).length > 0;
  }

  private static entityState(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform): unknown {
    const rawValue = entityArg.state ?? entityArg.nativeValue ?? entityArg.native_value ?? entityArg.value;
    if (platformArg === 'light' || platformArg === 'switch') {
      const value = entityArg.isOn ?? entityArg.is_on ?? rawValue;
      if (typeof value === 'boolean') {
        return value ? 'on' : 'off';
      }
      if (typeof value === 'string') {
        return value.toLowerCase() === 'true' ? 'on' : value.toLowerCase() === 'false' ? 'off' : value;
      }
      return value ?? 'unknown';
    }
    if (platformArg === 'cover') {
      const position = entityArg.currentCoverPosition ?? entityArg.current_cover_position ?? entityArg.position;
      if (typeof position === 'number') {
        return position;
      }
      const closed = entityArg.isClosed ?? entityArg.is_closed;
      return typeof closed === 'boolean' ? closed ? 'closed' : 'open' : rawValue ?? 'unknown';
    }
    if (platformArg === 'climate') {
      return entityArg.hvacMode || entityArg.hvac_mode || rawValue || 'unknown';
    }
    return rawValue ?? 'unknown';
  }

  private static entityAddresses(entityArg: IKnxEntityDescriptor): Record<string, string[]> {
    return {
      write: this.addressesFrom(entityArg, ['address', 'groupAddress', 'group_address', 'switchAddress', 'switch_address', 'moveLongAddress', 'move_long_address', 'positionAddress', 'position_address', 'targetTemperatureAddress', 'target_temperature_address', 'onOffAddress', 'on_off_address']),
      state: this.addressesFrom(entityArg, ['stateAddress', 'state_address', 'switchStateAddress', 'switch_state_address', 'brightnessStateAddress', 'brightness_state_address', 'positionStateAddress', 'position_state_address', 'targetTemperatureStateAddress', 'target_temperature_state_address', 'onOffStateAddress', 'on_off_state_address']),
    };
  }

  private static entityDeviceId(entityArg: IKnxEntityDescriptor): string {
    const explicit = entityArg.deviceId || entityArg.device_id;
    if (explicit) {
      return explicit;
    }
    return `knx.entity.${this.slug(entityArg.uniqueId || entityArg.unique_id || entityArg.entityId || entityArg.entity_id || this.firstEntityAddress(entityArg) || this.entityName(entityArg, this.corePlatform(entityArg.platform || 'sensor')))}`;
  }

  private static uniqueIdForEntity(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform): string {
    return entityArg.uniqueId || entityArg.unique_id || `knx_${platformArg}_${this.slug(this.firstEntityAddress(entityArg) || entityArg.entityId || entityArg.entity_id || this.entityName(entityArg, platformArg))}`;
  }

  private static deviceName(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform): string {
    return entityArg.name || entityArg.originalName || entityArg.original_name || `${this.connectionLabel(platformArg)} ${this.firstEntityAddress(entityArg) || 'entity'}`;
  }

  private static entityName(entityArg: IKnxEntityDescriptor, platformArg: TEntityPlatform): string {
    return entityArg.name || entityArg.originalName || entityArg.original_name || entityArg.fallbackName || entityArg.fallback_name || entityArg.entityId || entityArg.entity_id || `${this.connectionLabel(platformArg)} ${this.firstEntityAddress(entityArg) || 'entity'}`;
  }

  private static interfaceDeviceId(snapshotArg: IKnxSnapshot): string {
    const gateway = snapshotArg.gateways[0];
    const gatewayId = gateway ? this.gatewayId(gateway) : snapshotArg.connection.host || snapshotArg.connection.individualAddress || snapshotArg.connection.connectionType;
    return `knx.interface.${this.slug(String(gatewayId || 'automatic'))}`;
  }

  private static interfaceName(snapshotArg: IKnxSnapshot): string {
    const gateway = snapshotArg.gateways[0];
    return gateway?.name || (snapshotArg.connection.host ? `KNX/IP interface ${snapshotArg.connection.host}` : 'KNX interface');
  }

  private static gatewayId(gatewayArg: IKnxGatewayDescriptor): string {
    return gatewayArg.id || this.individualAddressString(gatewayArg.individualAddress || gatewayArg.individual_address) || gatewayArg.host || gatewayArg.ipAddr || gatewayArg.ip_addr || gatewayArg.ipAddress || gatewayArg.name || 'gateway';
  }

  private static firstEntityAddress(entityArg: IKnxEntityDescriptor): string | undefined {
    return this.addressesFrom(entityArg, ['address', 'groupAddress', 'group_address', 'stateAddress', 'state_address', 'switchAddress', 'switch_address', 'brightnessAddress', 'brightness_address', 'moveLongAddress', 'move_long_address', 'temperatureAddress', 'temperature_address'])[0];
  }

  private static addressesFrom(entityArg: IKnxEntityDescriptor, keysArg: string[]): string[] {
    const addresses: string[] = [];
    const record = entityArg as Record<string, unknown>;
    for (const key of keysArg) {
      addresses.push(...this.addressValues(record[key]));
    }
    return this.uniqueAddresses(addresses);
  }

  private static addressValues(valueArg: unknown): string[] {
    if (typeof valueArg === 'string' && valueArg.trim()) {
      return [valueArg.trim()];
    }
    if (Array.isArray(valueArg)) {
      return valueArg.filter((itemArg): itemArg is string => typeof itemArg === 'string' && Boolean(itemArg.trim())).map((itemArg) => itemArg.trim());
    }
    return [];
  }

  private static uniqueAddresses(addressesArg: string[]): string[] {
    return [...new Set(addressesArg.filter(Boolean))];
  }

  private static booleanPayload(valueArg: boolean, invertArg: boolean): boolean {
    return invertArg ? !valueArg : valueArg;
  }

  private static capabilityForPlatform(platformArg: TEntityPlatform): plugins.shxInterfaces.data.TDeviceCapability {
    if (platformArg === 'light') {
      return 'light';
    }
    if (platformArg === 'cover') {
      return 'cover';
    }
    if (platformArg === 'climate') {
      return 'climate';
    }
    return platformArg === 'switch' ? 'switch' : 'sensor';
  }

  private static deviceStateValue(valueArg: unknown): plugins.shxInterfaces.data.TDeviceStateValue {
    if (Array.isArray(valueArg)) {
      return JSON.stringify(valueArg);
    }
    if (typeof valueArg === 'string' || typeof valueArg === 'number' || typeof valueArg === 'boolean' || valueArg === null || this.isRecord(valueArg)) {
      return valueArg;
    }
    return valueArg === undefined ? null : String(valueArg);
  }

  private static connectionLabel(valueArg: unknown): string {
    return String(valueArg || 'KNX').replace(/_/g, ' ').replace(/\b\w/g, (matchArg) => matchArg.toUpperCase());
  }

  private static individualAddressString(valueArg: unknown): string | undefined {
    if (typeof valueArg === 'string') {
      return valueArg;
    }
    if (valueArg && typeof valueArg === 'object' && 'toString' in valueArg) {
      const rendered = String(valueArg);
      return rendered === '[object Object]' ? undefined : rendered;
    }
    return undefined;
  }

  private static numberValue(valueArg: unknown): number | undefined {
    return typeof valueArg === 'number' && Number.isFinite(valueArg) ? valueArg : undefined;
  }

  private static stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
  }

  private static asArray(valueArg: unknown): unknown[] {
    return Array.isArray(valueArg) ? valueArg : [];
  }

  private static asRecord(valueArg: unknown): Record<string, unknown> | undefined {
    return this.isRecord(valueArg) ? valueArg : undefined;
  }

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

  private static slug(valueArg: string): string {
    return valueArg.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'knx';
  }
}
