import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IIntegrationEvent, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import {
  broadlinkDefaultDelaySeconds,
  broadlinkDefaultPort,
  broadlinkDomain,
  broadlinkRemoteTypes,
  broadlinkRfRemoteTypes,
  broadlinkSensorDescriptions,
  broadlinkSwitchTypes,
} from './broadlink.constants.js';
import {
  broadlinkIrPacketFromTimings,
  broadlinkPacketFromBase64,
  broadlinkPacketFromBytes,
  broadlinkPacketFromHex,
  broadlinkRfPacketFromTimings,
} from './broadlink.packet.js';
import type {
  IBroadlinkCommand,
  IBroadlinkConfig,
  IBroadlinkCustomSwitchConfig,
  IBroadlinkDevice,
  IBroadlinkEntityDescriptor,
  IBroadlinkEvent,
  IBroadlinkPacket,
  IBroadlinkSnapshot,
  TBroadlinkCodeStore,
  TBroadlinkDeviceType,
} from './broadlink.types.js';

interface IBroadlinkSwitchDefinition {
  id: string;
  name: string;
  key?: string;
  slot?: number;
  method: 'send_data' | 'set_power' | 'set_state';
  custom?: IBroadlinkCustomSwitchConfig;
}

export class BroadlinkMapper {
  public static toSnapshot(configArg: IBroadlinkConfig, connectedArg?: boolean, eventsArg: IBroadlinkEvent[] = []): IBroadlinkSnapshot {
    const source = configArg.snapshot;
    const primaryDevice = this.primaryDevice(configArg, source);
    const devices = this.uniqueDevices([
      ...(source?.devices || []),
      ...(configArg.devices || []),
      ...(primaryDevice ? [primaryDevice] : []),
      ...this.devicesFromManualEntries(configArg),
    ]);

    return {
      connected: connectedArg ?? source?.connected ?? configArg.connected ?? devices.some((deviceArg) => deviceArg.available === true || deviceArg.online === true) ?? false,
      host: configArg.host || source?.host,
      port: configArg.port || source?.port || broadlinkDefaultPort,
      devices,
      entities: [...(source?.entities || []), ...(configArg.entities || [])],
      events: [...(source?.events || []), ...eventsArg],
      metadata: {
        ...source?.metadata,
        ...configArg.metadata,
        liveUdpAuthImplemented: false,
      },
    };
  }

  public static toDevices(snapshotArg: IBroadlinkSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    return snapshotArg.devices.map((deviceArg) => this.toDevice(deviceArg, snapshotArg));
  }

  public static toEntities(snapshotArg: IBroadlinkSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    const usedIds = new Map<string, number>();
    const seen = new Set<string>();
    const addEntity = (entityArg: IIntegrationEntity | undefined) => {
      if (!entityArg || seen.has(entityArg.id)) {
        return;
      }
      seen.add(entityArg.id);
      entities.push(entityArg);
    };

    for (const descriptor of snapshotArg.entities) {
      addEntity(this.entityFromDescriptor(snapshotArg, descriptor, usedIds));
    }

    for (const device of snapshotArg.devices) {
      const deviceId = this.deviceId(device);
      const baseName = this.deviceName(device);
      if (this.isRemote(device)) {
        addEntity(this.entity('remote' as TEntityPlatform, baseName, deviceId, this.uniqueId('remote', device), this.remoteState(device), usedIds, {
          ...this.baseAttributes(device),
          supportedFeatures: ['learn_command', 'delete_command', 'send_command'],
          supportsRf: this.supportsRf(device),
          codeDevices: Object.keys(this.codesForDevice(device)),
          writable: true,
        }, this.available(device)));
      }

      for (const switchDef of this.switchDefinitions(device)) {
        addEntity(this.entity('switch', switchDef.name, deviceId, `${this.uniqueId('switch', device)}_${this.slug(switchDef.id)}`, this.switchState(device, switchDef), usedIds, {
          ...this.baseAttributes(device),
          broadlinkSwitchId: switchDef.id,
          broadlinkSwitchKey: switchDef.key,
          broadlinkSlot: switchDef.slot,
          broadlinkMethod: switchDef.method,
          commandOn: switchDef.custom?.commandOn || switchDef.custom?.command_on,
          commandOff: switchDef.custom?.commandOff || switchDef.custom?.command_off,
          assumedState: switchDef.custom ? true : false,
          writable: true,
          ...switchDef.custom?.metadata,
        }, switchDef.custom?.available !== false && this.available(device), switchDef.custom?.entityId));
      }

      for (const sensor of this.sensorProperties(device)) {
        addEntity(this.sensorEntity(device, sensor, usedIds));
      }
    }

    return entities;
  }

  public static toIntegrationEvent(eventArg: IBroadlinkEvent): IIntegrationEvent {
    return {
      type: eventArg.type === 'command_failed' ? 'error' : eventArg.type === 'snapshot_refreshed' ? 'state_changed' : 'state_changed',
      integrationDomain: broadlinkDomain,
      deviceId: eventArg.deviceId,
      entityId: eventArg.entityId,
      data: eventArg,
      timestamp: eventArg.timestamp || Date.now(),
    };
  }

  public static commandForService(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    if (requestArg.domain === broadlinkDomain && ['send_packet', 'send_data'].includes(requestArg.service)) {
      return this.packetServiceCommand(snapshotArg, requestArg, requestArg.service === 'send_packet' ? 'send_packet' : 'send_data');
    }

    if ((requestArg.domain === broadlinkDomain && requestArg.service === 'send_ir') || (requestArg.domain === 'infrared' && requestArg.service === 'send_command')) {
      return this.irServiceCommand(snapshotArg, requestArg);
    }

    if ((requestArg.domain === broadlinkDomain && requestArg.service === 'send_rf') || (requestArg.domain === 'radio_frequency' && requestArg.service === 'send_command')) {
      return this.rfServiceCommand(snapshotArg, requestArg);
    }

    if (requestArg.domain === 'remote' && requestArg.service === 'send_command') {
      return this.remoteSendCommand(snapshotArg, requestArg);
    }

    if (requestArg.domain === 'remote' && requestArg.service === 'learn_command') {
      return this.learnCommand(snapshotArg, requestArg);
    }

    if (requestArg.domain === 'remote' && requestArg.service === 'delete_command') {
      return this.deleteCommand(snapshotArg, requestArg);
    }

    if (requestArg.domain === 'remote' && (requestArg.service === 'turn_on' || requestArg.service === 'turn_off')) {
      const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRemoteEntity(snapshotArg);
      if (!target) {
        return undefined;
      }
      return {
        type: requestArg.service === 'turn_on' ? 'remote_turn_on' : 'remote_turn_off',
        method: 'local_remote_enabled',
        service: requestArg.service,
        deviceId: target.deviceId,
        entityId: target.id,
        target: requestArg.target,
        data: requestArg.data,
        transmitted: false,
      };
    }

    if (requestArg.domain === 'switch' && (requestArg.service === 'turn_on' || requestArg.service === 'turn_off')) {
      return this.switchCommand(snapshotArg, requestArg);
    }

    return undefined;
  }

  public static deviceId(deviceArg: IBroadlinkDevice): string {
    return `broadlink.device.${this.slug(deviceArg.id || this.mac(deviceArg) || deviceArg.host || deviceArg.name || deviceArg.type || 'configured')}`;
  }

  public static slug(valueArg: unknown): string {
    const slug = String(valueArg || '')
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, '_')
      .replace(/^_+|_+$/g, '');
    return slug || 'unknown';
  }

  private static toDevice(deviceArg: IBroadlinkDevice, snapshotArg: IBroadlinkSnapshot): plugins.shxInterfaces.data.IDeviceDefinition {
    const updatedAt = deviceArg.updatedAt || new Date().toISOString();
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'availability', capability: 'sensor', name: 'Availability', readable: true, writable: false },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'availability', value: this.available(deviceArg) ? 'online' : 'offline', updatedAt },
    ];

    if (this.isRemote(deviceArg)) {
      features.push({ id: 'remote', capability: 'switch', name: 'Remote', readable: true, writable: true });
      this.pushDeviceState(state, 'remote', this.remoteState(deviceArg), updatedAt);
    }

    for (const switchDef of this.switchDefinitions(deviceArg)) {
      features.push({ id: `switch_${this.slug(switchDef.id)}`, capability: 'switch', name: switchDef.name, readable: true, writable: true });
      this.pushDeviceState(state, `switch_${this.slug(switchDef.id)}`, this.switchState(deviceArg, switchDef) === 'on', updatedAt);
    }

    for (const sensor of this.sensorProperties(deviceArg)) {
      const capability = sensor.key === 'power' || sensor.key === 'totalconsum' ? 'energy' : 'sensor';
      features.push({ id: sensor.key, capability, name: sensor.description.name, readable: true, writable: false, unit: sensor.description.unit });
      this.pushDeviceState(state, sensor.key, this.deviceStateValue(sensor.value), updatedAt);
    }

    return {
      id: this.deviceId(deviceArg),
      integrationDomain: broadlinkDomain,
      name: this.deviceName(deviceArg),
      protocol: 'unknown',
      manufacturer: deviceArg.manufacturer || 'Broadlink',
      model: deviceArg.model || deviceArg.type,
      online: snapshotArg.connected && this.available(deviceArg),
      features: this.uniqueFeatures(features),
      state,
      metadata: this.cleanAttributes({
        ...deviceArg.metadata,
        host: deviceArg.host || snapshotArg.host,
        port: deviceArg.port || snapshotArg.port || broadlinkDefaultPort,
        macAddress: this.mac(deviceArg),
        type: deviceArg.type,
        devtype: deviceArg.devtype,
        firmwareVersion: deviceArg.firmwareVersion,
        locked: deviceArg.locked ?? deviceArg.isLocked,
        authorized: deviceArg.authorized,
        liveUdpAuthImplemented: false,
      }),
    };
  }

  private static packetServiceCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest, typeArg: 'send_data' | 'send_packet'): IBroadlinkCommand | undefined {
    const packet = this.packetFromData(requestArg.data);
    const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRemoteEntity(snapshotArg);
    if (!packet || !target) {
      return undefined;
    }
    return {
      type: typeArg,
      method: 'send_data',
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      packet,
      packets: [packet],
      numRepeats: this.positiveInteger(requestArg.data?.num_repeats ?? requestArg.data?.numRepeats) || 1,
      delaySecs: this.numberData(requestArg.data, 'delay_secs') ?? this.numberData(requestArg.data, 'delaySecs') ?? broadlinkDefaultDelaySeconds,
    };
  }

  private static irServiceCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    const packet = this.packetFromData(requestArg.data) || this.irPacketFromData(requestArg.data);
    const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRemoteEntity(snapshotArg);
    if (!packet || !target) {
      return undefined;
    }
    return {
      type: 'send_ir',
      method: 'send_data',
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      packet,
      packets: [packet],
      commandType: 'ir',
    };
  }

  private static rfServiceCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    const packet = this.packetFromData(requestArg.data) || this.rfPacketFromData(requestArg.data);
    const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRfRemoteEntity(snapshotArg);
    if (!packet || !target) {
      return undefined;
    }
    return {
      type: 'send_rf',
      method: 'send_data',
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      packet,
      packets: [packet],
      commandType: 'rf',
    };
  }

  private static remoteSendCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRemoteEntity(snapshotArg);
    const device = target ? this.deviceById(snapshotArg, target.deviceId) : undefined;
    const remoteDevice = this.stringValue(requestArg.data?.device || requestArg.data?.remoteDevice);
    const commandNames = this.commandList(requestArg.data?.command);
    if (!target || !commandNames.length) {
      return undefined;
    }
    const packets = this.packetsForCommands(snapshotArg, device, commandNames, remoteDevice);
    if (!packets) {
      return undefined;
    }
    return {
      type: 'send_data',
      method: 'send_data',
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      packet: packets[0],
      packets,
      commandNames,
      remoteDevice,
      numRepeats: this.positiveInteger(requestArg.data?.num_repeats ?? requestArg.data?.numRepeats) || 1,
      delaySecs: this.numberData(requestArg.data, 'delay_secs') ?? this.numberData(requestArg.data, 'delaySecs') ?? broadlinkDefaultDelaySeconds,
    };
  }

  private static learnCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRemoteEntity(snapshotArg);
    const device = target ? this.deviceById(snapshotArg, target.deviceId) : undefined;
    const commandType = this.stringValue(requestArg.data?.command_type || requestArg.data?.commandType) === 'rf' ? 'rf' : 'ir';
    const remoteDevice = this.stringValue(requestArg.data?.device || requestArg.data?.remoteDevice);
    const commandNames = this.commandList(requestArg.data?.command);
    if (!target || !remoteDevice || !commandNames.length) {
      return undefined;
    }
    if (commandType === 'rf' && device && !this.supportsRf(device)) {
      return undefined;
    }
    return {
      type: commandType === 'rf' ? 'learn_rf' : 'learn_ir',
      method: commandType === 'rf' ? 'sweep_frequency/find_rf_packet/check_data' : 'enter_learning/check_data',
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      commandNames,
      remoteDevice,
      commandType,
      alternative: this.booleanValue(requestArg.data?.alternative) || false,
      metadata: {
        learningTimeoutSeconds: 30,
        requiresLiveTransport: true,
      },
    };
  }

  private static deleteCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    const target = this.findTargetEntity(snapshotArg, requestArg) || this.firstRemoteEntity(snapshotArg);
    const remoteDevice = this.stringValue(requestArg.data?.device || requestArg.data?.remoteDevice);
    const commandNames = this.commandList(requestArg.data?.command);
    if (!target || !remoteDevice || !commandNames.length) {
      return undefined;
    }
    return {
      type: 'delete_command',
      method: 'local_delete_code',
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      commandNames,
      remoteDevice,
      transmitted: false,
    };
  }

  private static switchCommand(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IBroadlinkCommand | undefined {
    const target = this.findTargetEntity(snapshotArg, requestArg);
    const device = target ? this.deviceById(snapshotArg, target.deviceId) : undefined;
    if (!target || !device) {
      return undefined;
    }
    const switchId = this.stringValue(target.attributes?.broadlinkSwitchId);
    const switchDef = this.switchDefinitions(device).find((definitionArg) => definitionArg.id === switchId);
    if (!switchDef) {
      return undefined;
    }
    const turnOn = requestArg.service === 'turn_on';
    if (switchDef.method === 'send_data') {
      const packetSource = turnOn ? switchDef.custom?.commandOn || switchDef.custom?.command_on : switchDef.custom?.commandOff || switchDef.custom?.command_off;
      if (!packetSource) {
        return undefined;
      }
      const packet = broadlinkPacketFromBase64(packetSource);
      return {
        type: 'send_data',
        method: 'send_data',
        service: requestArg.service,
        deviceId: target.deviceId,
        entityId: target.id,
        target: requestArg.target,
        data: requestArg.data,
        packet,
        packets: [packet],
      };
    }
    const payload = switchDef.method === 'set_state'
      ? { [switchDef.key || 'pwr']: turnOn }
      : switchDef.slot ? { slot: switchDef.slot, pwr: turnOn } : { pwr: turnOn };
    return {
      type: switchDef.method,
      method: switchDef.method,
      service: requestArg.service,
      deviceId: target.deviceId,
      entityId: target.id,
      target: requestArg.target,
      data: requestArg.data,
      payload,
    };
  }

  private static entityFromDescriptor(snapshotArg: IBroadlinkSnapshot, entityArg: IBroadlinkEntityDescriptor, usedIdsArg: Map<string, number>): IIntegrationEntity {
    const platform = this.corePlatform(entityArg.platform || 'sensor');
    const name = entityArg.name || entityArg.entityId || entityArg.id || 'Broadlink entity';
    return this.entity(platform, name, entityArg.deviceId || this.firstDeviceId(snapshotArg), entityArg.uniqueId || `broadlink_${this.slug(entityArg.id || entityArg.entityId || name)}`, entityArg.state ?? null, usedIdsArg, entityArg.attributes, entityArg.available !== false, entityArg.entityId || entityArg.id);
  }

  private static sensorEntity(deviceArg: IBroadlinkDevice, sensorArg: ReturnType<typeof BroadlinkMapper.sensorProperties>[number], usedIdsArg: Map<string, number>): IIntegrationEntity {
    const baseName = this.deviceName(deviceArg);
    return this.entity('sensor', `${baseName} ${sensorArg.description.name}`, this.deviceId(deviceArg), `${this.uniqueId('sensor', deviceArg)}_${this.slug(sensorArg.key)}`, sensorArg.value ?? null, usedIdsArg, {
      ...this.baseAttributes(deviceArg),
      broadlinkSensorKey: sensorArg.key,
      unitOfMeasurement: sensorArg.description.unit,
      deviceClass: sensorArg.description.deviceClass,
      stateClass: sensorArg.description.stateClass,
    }, this.available(deviceArg));
  }

  private static entity(platformArg: TEntityPlatform, nameArg: string, deviceIdArg: string, uniqueIdArg: string, stateArg: unknown, usedIdsArg: Map<string, number>, attributesArg?: Record<string, unknown>, availableArg = true, requestedEntityIdArg?: string): IIntegrationEntity {
    return {
      id: requestedEntityIdArg || this.uniqueEntityId(platformArg, nameArg, usedIdsArg),
      uniqueId: uniqueIdArg,
      integrationDomain: broadlinkDomain,
      deviceId: deviceIdArg,
      platform: platformArg,
      name: nameArg,
      state: this.entityState(stateArg, platformArg),
      attributes: this.cleanAttributes(attributesArg || {}),
      available: availableArg,
    };
  }

  private static primaryDevice(configArg: IBroadlinkConfig, sourceArg?: IBroadlinkSnapshot): IBroadlinkDevice | undefined {
    if (!configArg.host && !configArg.name && !configArg.macAddress && !configArg.mac && !configArg.type && !configArg.state && !configArg.sensors && !configArg.codes && !configArg.switches?.length) {
      return undefined;
    }
    return {
      id: configArg.deviceId || configArg.macAddress || configArg.mac || configArg.host || 'configured',
      host: configArg.host || sourceArg?.host,
      port: configArg.port || sourceArg?.port || broadlinkDefaultPort,
      macAddress: configArg.macAddress || configArg.mac,
      type: configArg.type,
      devtype: configArg.devtype,
      name: configArg.name,
      model: configArg.model || configArg.type,
      manufacturer: configArg.manufacturer || 'Broadlink',
      firmwareVersion: configArg.firmwareVersion,
      locked: configArg.locked ?? configArg.isLocked,
      authorized: configArg.authorized,
      available: configArg.connected,
      online: configArg.connected,
      state: configArg.state,
      sensors: configArg.sensors,
      codes: configArg.codes,
      flags: configArg.flags,
      switches: configArg.switches,
      metadata: configArg.metadata,
    };
  }

  private static devicesFromManualEntries(configArg: IBroadlinkConfig): IBroadlinkDevice[] {
    const devices: IBroadlinkDevice[] = [];
    for (const entry of configArg.manualEntries || []) {
      if (entry.snapshot?.devices) {
        devices.push(...entry.snapshot.devices);
      }
      if (entry.devices?.length) {
        devices.push(...entry.devices);
      }
      if (entry.device) {
        devices.push(entry.device);
      }
      if (entry.host || entry.macAddress || entry.mac || entry.type || entry.state || entry.sensors || entry.codes || entry.switches?.length) {
        devices.push({
          id: entry.deviceId || entry.id || entry.macAddress || entry.mac || entry.host,
          host: entry.host,
          port: entry.port || broadlinkDefaultPort,
          macAddress: entry.macAddress || entry.mac,
          type: entry.type,
          devtype: entry.devtype,
          name: entry.name,
          model: entry.model || entry.type,
          manufacturer: entry.manufacturer || 'Broadlink',
          locked: entry.locked ?? entry.isLocked,
          state: entry.state,
          sensors: entry.sensors,
          codes: entry.codes,
          switches: entry.switches,
          metadata: entry.metadata,
        });
      }
    }
    return devices;
  }

  private static uniqueDevices(devicesArg: IBroadlinkDevice[]): IBroadlinkDevice[] {
    const seen = new Set<string>();
    const devices: IBroadlinkDevice[] = [];
    for (const device of devicesArg) {
      const id = this.deviceId(device);
      if (seen.has(id)) {
        continue;
      }
      seen.add(id);
      devices.push(device);
    }
    return devices;
  }

  private static switchDefinitions(deviceArg: IBroadlinkDevice): IBroadlinkSwitchDefinition[] {
    const type = this.deviceType(deviceArg);
    const definitions: IBroadlinkSwitchDefinition[] = [];
    if (['SP1', 'SP2', 'SP2S', 'SP3', 'SP3S'].includes(type)) {
      definitions.push({ id: 'pwr', name: this.deviceName(deviceArg), key: 'pwr', method: 'set_power' });
    } else if (['SP4', 'SP4B'].includes(type)) {
      definitions.push({ id: 'pwr', name: this.deviceName(deviceArg), key: 'pwr', method: 'set_state' });
    } else if (['MP1', 'MP1S'].includes(type)) {
      for (const slot of [1, 2, 3, 4]) {
        definitions.push({ id: `s${slot}`, name: `${this.deviceName(deviceArg)} S${slot}`, key: `s${slot}`, slot, method: 'set_power' });
      }
    } else if (type === 'BG1') {
      for (const slot of [1, 2]) {
        definitions.push({ id: `pwr${slot}`, name: `${this.deviceName(deviceArg)} S${slot}`, key: `pwr${slot}`, slot, method: 'set_state' });
      }
    }
    for (const customSwitch of deviceArg.switches || []) {
      definitions.push({ id: customSwitch.id || customSwitch.name, name: customSwitch.name, method: 'send_data', custom: customSwitch });
    }
    return definitions;
  }

  private static sensorProperties(deviceArg: IBroadlinkDevice): Array<{ key: string; value: unknown; description: typeof broadlinkSensorDescriptions[string] }> {
    const state = { ...(deviceArg.state || {}), ...(deviceArg.sensors || {}) };
    const type = this.deviceType(deviceArg);
    const properties: Array<{ key: string; value: unknown; description: typeof broadlinkSensorDescriptions[string] }> = [];
    for (const [key, description] of Object.entries(broadlinkSensorDescriptions)) {
      if (!(key in state)) {
        continue;
      }
      const value = state[key];
      if ((type === 'RM4PRO' || type === 'RM4MINI') && (key === 'temperature' || key === 'humidity') && value === 0) {
        continue;
      }
      properties.push({ key, value, description });
    }
    return properties;
  }

  private static packetsForCommands(snapshotArg: IBroadlinkSnapshot, deviceArg: IBroadlinkDevice | undefined, commandsArg: string[], remoteDeviceArg?: string): IBroadlinkPacket[] | undefined {
    const packets: IBroadlinkPacket[] = [];
    for (const command of commandsArg) {
      if (command.startsWith('b64:')) {
        packets.push(broadlinkPacketFromBase64(command));
        continue;
      }
      const code = this.lookupCode(snapshotArg, deviceArg, remoteDeviceArg, command);
      if (!code) {
        return undefined;
      }
      const selected = Array.isArray(code) ? code[this.toggleIndex(deviceArg, remoteDeviceArg, code.length)] : code;
      packets.push(broadlinkPacketFromBase64(selected));
    }
    return packets;
  }

  private static lookupCode(snapshotArg: IBroadlinkSnapshot, deviceArg: IBroadlinkDevice | undefined, remoteDeviceArg: string | undefined, commandArg: string): string | string[] | undefined {
    const stores: TBroadlinkCodeStore[] = [];
    if (deviceArg?.codes) {
      stores.push(deviceArg.codes);
    }
    for (const device of snapshotArg.devices) {
      if (device.codes && device !== deviceArg) {
        stores.push(device.codes);
      }
    }
    for (const store of stores) {
      if (remoteDeviceArg && store[remoteDeviceArg]?.[commandArg]) {
        return store[remoteDeviceArg][commandArg];
      }
      if (!remoteDeviceArg) {
        for (const codes of Object.values(store)) {
          if (codes[commandArg]) {
            return codes[commandArg];
          }
        }
      }
    }
    return undefined;
  }

  private static packetFromData(dataArg?: Record<string, unknown>): IBroadlinkPacket | undefined {
    const value = dataArg?.packet ?? dataArg?.code ?? dataArg?.base64 ?? dataArg?.rawData ?? dataArg?.data;
    if (this.isRecord(value)) {
      if (typeof value.base64 === 'string') {
        return broadlinkPacketFromBase64(value.base64);
      }
      if (typeof value.hex === 'string') {
        return broadlinkPacketFromHex(value.hex);
      }
      if (Array.isArray(value.bytes)) {
        return broadlinkPacketFromBytes(value.bytes.filter((itemArg): itemArg is number => typeof itemArg === 'number'));
      }
    }
    if (Array.isArray(value)) {
      return broadlinkPacketFromBytes(value.filter((itemArg): itemArg is number => typeof itemArg === 'number'));
    }
    if (typeof dataArg?.hex === 'string') {
      return broadlinkPacketFromHex(dataArg.hex);
    }
    if (typeof value === 'string' && value.trim()) {
      const text = value.trim();
      return text.startsWith('hex:') || /^[0-9a-f\s:]+$/i.test(text) && text.replace(/[^0-9a-f]/gi, '').length % 2 === 0
        ? broadlinkPacketFromHex(text.replace(/^hex:/i, ''))
        : broadlinkPacketFromBase64(text);
    }
    return undefined;
  }

  private static irPacketFromData(dataArg?: Record<string, unknown>): IBroadlinkPacket | undefined {
    const timings = this.timingsFromData(dataArg);
    return timings?.length ? broadlinkIrPacketFromTimings(timings) : undefined;
  }

  private static rfPacketFromData(dataArg?: Record<string, unknown>): IBroadlinkPacket | undefined {
    const timings = this.timingsFromData(dataArg);
    const frequency = this.numberData(dataArg, 'frequency') ?? this.numberData(dataArg, 'carrier_frequency') ?? this.numberData(dataArg, 'carrierFrequency');
    if (!timings?.length || frequency === undefined) {
      return undefined;
    }
    try {
      return broadlinkRfPacketFromTimings({
        frequency,
        timings,
        repeatCount: this.positiveInteger(dataArg?.repeat_count ?? dataArg?.repeatCount) || 0,
      });
    } catch {
      return undefined;
    }
  }

  private static timingsFromData(dataArg?: Record<string, unknown>): number[] | undefined {
    const command = this.isRecord(dataArg?.command) ? dataArg.command : undefined;
    const value = dataArg?.rawTimings ?? dataArg?.raw_timings ?? dataArg?.timings ?? dataArg?.pulses ?? command?.rawTimings ?? command?.raw_timings;
    return Array.isArray(value) ? value.filter((itemArg): itemArg is number => typeof itemArg === 'number' && Number.isFinite(itemArg)) : undefined;
  }

  private static findTargetEntity(snapshotArg: IBroadlinkSnapshot, requestArg: IServiceCallRequest): IIntegrationEntity | undefined {
    const entityId = requestArg.target.entityId || this.stringValue(requestArg.data?.entity_id || requestArg.data?.entityId);
    const deviceId = requestArg.target.deviceId || this.stringValue(requestArg.data?.device_id || requestArg.data?.deviceId);
    const entities = this.toEntities(snapshotArg);
    if (entityId) {
      return entities.find((entityArg) => entityArg.id === entityId || entityArg.uniqueId === entityId);
    }
    if (deviceId) {
      return entities.find((entityArg) => entityArg.deviceId === deviceId || entityArg.uniqueId === deviceId);
    }
    return undefined;
  }

  private static firstRemoteEntity(snapshotArg: IBroadlinkSnapshot): IIntegrationEntity | undefined {
    return this.toEntities(snapshotArg).find((entityArg) => entityArg.platform === ('remote' as TEntityPlatform));
  }

  private static firstRfRemoteEntity(snapshotArg: IBroadlinkSnapshot): IIntegrationEntity | undefined {
    const entities = this.toEntities(snapshotArg);
    return entities.find((entityArg) => entityArg.platform === ('remote' as TEntityPlatform) && entityArg.attributes?.supportsRf === true);
  }

  private static firstDeviceId(snapshotArg: IBroadlinkSnapshot): string {
    return snapshotArg.devices[0] ? this.deviceId(snapshotArg.devices[0]) : 'broadlink.device.configured';
  }

  private static deviceById(snapshotArg: IBroadlinkSnapshot, deviceIdArg: string): IBroadlinkDevice | undefined {
    return snapshotArg.devices.find((deviceArg) => this.deviceId(deviceArg) === deviceIdArg || deviceArg.id === deviceIdArg);
  }

  private static isRemote(deviceArg: IBroadlinkDevice): boolean {
    return broadlinkRemoteTypes.has(this.deviceType(deviceArg)) || Boolean(deviceArg.codes) || Boolean(deviceArg.switches?.length);
  }

  private static supportsRf(deviceArg: IBroadlinkDevice): boolean {
    return deviceArg.supportsRf === true || broadlinkRfRemoteTypes.has(this.deviceType(deviceArg));
  }

  private static available(deviceArg: IBroadlinkDevice): boolean {
    return deviceArg.available !== false && deviceArg.online !== false;
  }

  private static remoteState(deviceArg: IBroadlinkDevice): string {
    const state = this.stringValue(deviceArg.state?.remoteState || deviceArg.state?.remote);
    if (state === 'off') {
      return 'off';
    }
    return 'on';
  }

  private static switchState(deviceArg: IBroadlinkDevice, switchDefArg: IBroadlinkSwitchDefinition): string {
    const customState = switchDefArg.custom?.state;
    const state = deviceArg.state || {};
    const value = customState ?? (switchDefArg.key ? state[switchDefArg.key] : undefined) ?? state.pwr ?? state.power;
    return this.onState(value) ? 'on' : 'off';
  }

  private static onState(valueArg: unknown): boolean {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (typeof valueArg === 'number') {
      return valueArg !== 0;
    }
    return ['on', 'true', '1', 'open'].includes(String(valueArg || '').toLowerCase());
  }

  private static codesForDevice(deviceArg: IBroadlinkDevice): TBroadlinkCodeStore {
    return deviceArg.codes || {};
  }

  private static toggleIndex(deviceArg: IBroadlinkDevice | undefined, remoteDeviceArg: string | undefined, lengthArg: number): number {
    const value = remoteDeviceArg ? deviceArg?.flags?.[remoteDeviceArg] : undefined;
    return typeof value === 'number' && Number.isFinite(value) && lengthArg > 0 ? Math.abs(Math.floor(value)) % lengthArg : 0;
  }

  private static baseAttributes(deviceArg: IBroadlinkDevice): Record<string, unknown> {
    return this.cleanAttributes({
      host: deviceArg.host,
      port: deviceArg.port || broadlinkDefaultPort,
      macAddress: this.mac(deviceArg),
      type: deviceArg.type,
      devtype: deviceArg.devtype,
      model: deviceArg.model,
      firmwareVersion: deviceArg.firmwareVersion,
      locked: deviceArg.locked ?? deviceArg.isLocked,
      authorized: deviceArg.authorized,
    });
  }

  private static deviceName(deviceArg: IBroadlinkDevice): string {
    return deviceArg.name || deviceArg.model || deviceArg.type || this.mac(deviceArg) || deviceArg.host || 'Broadlink device';
  }

  private static uniqueId(platformArg: string, deviceArg: IBroadlinkDevice): string {
    return `broadlink_${platformArg}_${this.slug(deviceArg.id || this.mac(deviceArg) || deviceArg.host || this.deviceName(deviceArg))}`;
  }

  private static uniqueEntityId(platformArg: TEntityPlatform, nameArg: string, usedIdsArg: Map<string, number>): string {
    const base = `${platformArg}.${this.slug(nameArg)}`;
    const count = usedIdsArg.get(base) || 0;
    usedIdsArg.set(base, count + 1);
    return count === 0 ? base : `${base}_${count + 1}`;
  }

  private static deviceType(deviceArg: IBroadlinkDevice): TBroadlinkDeviceType {
    return String(deviceArg.type || deviceArg.model || '').toUpperCase();
  }

  private static mac(deviceArg: IBroadlinkDevice): string | undefined {
    const value = deviceArg.macAddress || deviceArg.mac;
    if (!value) {
      return undefined;
    }
    const compact = value.replace(/[^0-9a-f]/gi, '').toLowerCase();
    return compact.length === 12 ? compact.match(/.{1,2}/g)?.join(':') : value;
  }

  private static corePlatform(valueArg: unknown): TEntityPlatform {
    const platform = String(valueArg || 'sensor');
    const supported = ['light', 'switch', 'sensor', 'binary_sensor', 'button', 'climate', 'cover', 'fan', 'media_player', 'number', 'select', 'text', 'update', 'remote'];
    return supported.includes(platform) ? platform as TEntityPlatform : 'sensor';
  }

  private static entityState(valueArg: unknown, platformArg: TEntityPlatform): unknown {
    if (platformArg === 'switch' || platformArg === 'binary_sensor' || platformArg === 'light') {
      return this.onState(valueArg) ? 'on' : 'off';
    }
    return valueArg;
  }

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

  private static pushDeviceState(stateArg: plugins.shxInterfaces.data.IDeviceState[], featureIdArg: string, valueArg: unknown, updatedAtArg: string): void {
    if (valueArg === undefined) {
      return;
    }
    stateArg.push({ featureId: featureIdArg, value: this.deviceStateValue(valueArg), updatedAt: updatedAtArg });
  }

  private static uniqueFeatures(featuresArg: plugins.shxInterfaces.data.IDeviceFeature[]): plugins.shxInterfaces.data.IDeviceFeature[] {
    const seen = new Set<string>();
    return featuresArg.filter((featureArg) => {
      if (seen.has(featureArg.id)) {
        return false;
      }
      seen.add(featureArg.id);
      return true;
    });
  }

  private static cleanAttributes(valueArg: Record<string, unknown>): Record<string, unknown> {
    return Object.fromEntries(Object.entries(valueArg).filter(([, value]) => value !== undefined));
  }

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

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

  private static numberData(dataArg: Record<string, unknown> | undefined, keyArg: string): number | undefined {
    const value = dataArg?.[keyArg];
    if (typeof value === 'number' && Number.isFinite(value)) {
      return value;
    }
    if (typeof value === 'string' && value.trim()) {
      const parsed = Number(value);
      return Number.isFinite(parsed) ? parsed : undefined;
    }
    return undefined;
  }

  private static positiveInteger(valueArg: unknown): number | undefined {
    if (typeof valueArg === 'number' && Number.isFinite(valueArg) && valueArg > 0) {
      return Math.floor(valueArg);
    }
    if (typeof valueArg === 'string' && valueArg.trim()) {
      const value = Number(valueArg);
      return Number.isFinite(value) && value > 0 ? Math.floor(value) : undefined;
    }
    return undefined;
  }

  private static booleanValue(valueArg: unknown): boolean | undefined {
    return typeof valueArg === 'boolean' ? valueArg : undefined;
  }

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