import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type { ITradfriAirPurifier, ITradfriBlind, ITradfriCommand, ITradfriDevice, ITradfriDeviceInfo, ITradfriGateway, ITradfriGroup, ITradfriLight, ITradfriSensor, ITradfriSnapshot, ITradfriSocket, TTradfriResourceType } from './tradfri.types.js';

const ROOT_DEVICES = '15001';
const ROOT_GROUPS = '15004';
const ATTR_ID = '9003';
const ATTR_NAME = '9001';
const ATTR_DEVICE_INFO = '3';
const ATTR_REACHABLE_STATE = '9019';
const ATTR_DEVICE_MANUFACTURER = '0';
const ATTR_DEVICE_MODEL_NUMBER = '1';
const ATTR_DEVICE_SERIAL = '2';
const ATTR_DEVICE_FIRMWARE_VERSION = '3';
const ATTR_DEVICE_BATTERY = '9';
const ATTR_DEVICE_STATE = '5850';
const ATTR_LIGHT_CONTROL = '3311';
const ATTR_LIGHT_DIMMER = '5851';
const ATTR_LIGHT_COLOR_HEX = '5706';
const ATTR_LIGHT_COLOR_HUE = '5707';
const ATTR_LIGHT_COLOR_SATURATION = '5708';
const ATTR_LIGHT_MIREDS = '5711';
const ATTR_SWITCH_PLUG = '3312';
const ATTR_SENSOR = '3300';
const ATTR_SENSOR_VALUE = '5700';
const ATTR_SENSOR_UNIT = '5701';
const ATTR_SENSOR_TYPE = '5751';
const ATTR_START_BLINDS = '15015';
const ATTR_BLIND_CURRENT_POSITION = '5536';
const ROOT_AIR_PURIFIER = '15025';
const ATTR_AIR_PURIFIER_MODE = '5900';
const ATTR_AIR_PURIFIER_AIR_QUALITY = '5907';
const ATTR_AIR_PURIFIER_FAN_SPEED = '5908';
const ATTR_AIR_PURIFIER_FILTER_LIFETIME_REMAINING = '5910';
const ATTR_GROUP_MEMBERS = '9018';
const ATTR_HS_LINK = '15002';

export class TradfriMapper {
  public static toDevices(snapshotArg: ITradfriSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = new Date().toISOString();
    const gateway = this.gateway(snapshotArg);
    const gatewayId = this.gatewayIdentifier(snapshotArg);
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [this.gatewayDevice(snapshotArg, gateway, updatedAt)];

    for (const [index, device] of snapshotArg.devices.entries()) {
      const deviceResourceId = this.deviceResourceId(device, index);
      const deviceId = this.deviceId(snapshotArg, device, index);
      const deviceInfo = this.deviceInfo(device);
      const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
        { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
      ];
      const state: plugins.shxInterfaces.data.IDeviceState[] = [
        { featureId: 'connectivity', value: this.reachable(device) ? 'online' : 'offline', updatedAt },
      ];

      this.pushOptionalFeature(features, state, 'firmware', 'Firmware', this.stringValue(deviceInfo.firmwareVersion || deviceInfo.firmware_version), undefined, updatedAt);
      this.pushOptionalFeature(features, state, 'battery', 'Battery', this.numberValue(deviceInfo.batteryLevel ?? deviceInfo.battery_level), '%', updatedAt);

      for (const [lightIndex, light] of this.lightControls(device).entries()) {
        const prefix = this.indexedFeatureId('light', lightIndex);
        features.push({ id: `${prefix}_on`, capability: 'light', name: this.indexedName('Light', lightIndex), readable: true, writable: true });
        state.push({ featureId: `${prefix}_on`, value: this.booleanState(this.value(light, ['state', 'on', ATTR_DEVICE_STATE])), updatedAt });
        this.pushOptionalFeature(features, state, `${prefix}_brightness`, `${this.indexedName('Light', lightIndex)} brightness`, this.lightBrightnessPercent(light), '%', updatedAt, 'light', true);
        this.pushOptionalFeature(features, state, `${prefix}_color_temp`, `${this.indexedName('Light', lightIndex)} color temperature`, this.numberValue(this.value(light, ['colorTemp', 'color_temp', 'colorMireds', 'color_mireds', ATTR_LIGHT_MIREDS])), 'mired', updatedAt, 'light', true);
      }

      for (const [socketIndex, socket] of this.socketControls(device).entries()) {
        const prefix = this.indexedFeatureId('outlet', socketIndex);
        features.push({ id: `${prefix}_on`, capability: 'switch', name: this.indexedName('Outlet', socketIndex), readable: true, writable: true });
        state.push({ featureId: `${prefix}_on`, value: this.booleanState(this.value(socket, ['state', 'on', ATTR_DEVICE_STATE])), updatedAt });
      }

      for (const [blindIndex, blind] of this.blindControls(device).entries()) {
        const prefix = this.indexedFeatureId('cover', blindIndex);
        features.push({ id: `${prefix}_position`, capability: 'cover', name: this.indexedName('Blind position', blindIndex), readable: true, writable: true, unit: '%' });
        state.push({ featureId: `${prefix}_position`, value: this.coverPosition(blind), updatedAt });
      }

      for (const [fanIndex, fan] of this.airPurifierControls(device).entries()) {
        const prefix = this.indexedFeatureId('fan', fanIndex);
        features.push({ id: `${prefix}_speed`, capability: 'fan', name: this.indexedName('Air purifier fan', fanIndex), readable: true, writable: true, unit: '%' });
        state.push({ featureId: `${prefix}_speed`, value: this.fanPercentage(fan), updatedAt });
        this.pushOptionalFeature(features, state, `${prefix}_air_quality`, `${this.indexedName('Air quality', fanIndex)}`, this.airQuality(fan), 'ug/m3', updatedAt);
      }

      for (const [sensorIndex, sensor] of this.sensorControls(device).entries()) {
        const sensorName = this.sensorName(sensor, sensorIndex);
        const sensorFeature = this.indexedFeatureId(this.binarySensor(sensor) ? 'binary_sensor' : 'sensor', sensorIndex);
        features.push({ id: sensorFeature, capability: 'sensor', name: sensorName, readable: true, writable: false, unit: this.sensorUnit(sensor) });
        state.push({ featureId: sensorFeature, value: this.deviceStateValue(this.sensorValue(sensor)), updatedAt });
      }

      devices.push({
        id: deviceId,
        integrationDomain: 'tradfri',
        name: this.deviceName(device, deviceResourceId),
        protocol: 'zigbee',
        manufacturer: this.stringValue(deviceInfo.manufacturer) || 'IKEA of Sweden',
        model: this.stringValue(deviceInfo.modelNumber || deviceInfo.model_number),
        online: this.reachable(device),
        features,
        state,
        metadata: {
          gatewayId,
          tradfriDeviceId: deviceResourceId,
          serial: deviceInfo.serial,
          powerSource: deviceInfo.powerSource ?? deviceInfo.power_source,
          rawDeviceInfo: deviceInfo.raw,
          transport: 'coap-dtls',
        },
      });
    }

    for (const [index, group] of snapshotArg.groups.entries()) {
      const groupResourceId = this.groupResourceId(group, index);
      devices.push({
        id: this.groupDeviceId(snapshotArg, group, index),
        integrationDomain: 'tradfri',
        name: this.groupName(group, groupResourceId),
        protocol: 'zigbee',
        manufacturer: 'IKEA of Sweden',
        model: 'TRADFRI group',
        online: snapshotArg.connected ?? true,
        features: [
          { id: 'on', capability: 'light', name: 'Power', readable: true, writable: true },
          { id: 'brightness', capability: 'light', name: 'Brightness', readable: true, writable: true, unit: '%' },
          { id: 'member_count', capability: 'sensor', name: 'Member count', readable: true, writable: false },
        ],
        state: [
          { featureId: 'on', value: this.booleanState(this.value(group, ['state', ATTR_DEVICE_STATE])), updatedAt },
          { featureId: 'brightness', value: this.groupBrightnessPercent(group) ?? null, updatedAt },
          { featureId: 'member_count', value: this.groupMemberIds(group).length, updatedAt },
        ],
        metadata: {
          gatewayId,
          tradfriGroupId: groupResourceId,
          memberIds: this.groupMemberIds(group),
          transport: 'coap-dtls',
        },
      });
    }

    return devices;
  }

  public static toEntities(snapshotArg: ITradfriSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    for (const [index, device] of snapshotArg.devices.entries()) {
      const deviceId = this.deviceId(snapshotArg, device, index);
      const deviceResourceId = this.deviceResourceId(device, index);
      const deviceSlug = this.slug(this.deviceName(device, deviceResourceId));
      const reachable = this.reachable(device);
      const deviceInfo = this.deviceInfo(device);

      for (const [lightIndex, light] of this.lightControls(device).entries()) {
        entities.push({
          id: `light.${this.entitySlug(deviceSlug, lightIndex)}`,
          uniqueId: `tradfri_light_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_${lightIndex}`,
          integrationDomain: 'tradfri',
          deviceId,
          platform: 'light',
          name: this.indexedEntityName(this.deviceName(device, deviceResourceId), lightIndex),
          state: this.booleanState(this.value(light, ['state', 'on', ATTR_DEVICE_STATE])) ? 'on' : 'off',
          attributes: {
            brightness: this.numberValue(this.value(light, ['dimmer', 'brightness', ATTR_LIGHT_DIMMER])),
            brightnessPercent: this.lightBrightnessPercent(light),
            colorTempMireds: this.numberValue(this.value(light, ['colorTemp', 'color_temp', 'colorMireds', 'color_mireds', ATTR_LIGHT_MIREDS])),
            colorHex: this.stringValue(this.value(light, ['colorHex', 'color_hex', ATTR_LIGHT_COLOR_HEX])),
            hue: this.numberValue(this.value(light, ['hue', 'colorHue', 'color_hue', ATTR_LIGHT_COLOR_HUE])),
            saturation: this.numberValue(this.value(light, ['saturation', 'colorSaturation', 'color_saturation', ATTR_LIGHT_COLOR_SATURATION])),
            tradfriResourceType: 'device',
            tradfriResourceId: deviceResourceId,
            tradfriControl: 'light',
            tradfriIndex: lightIndex,
          },
          available: reachable,
        });
      }

      for (const [socketIndex, socket] of this.socketControls(device).entries()) {
        entities.push({
          id: `switch.${this.entitySlug(deviceSlug, socketIndex)}`,
          uniqueId: `tradfri_switch_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_${socketIndex}`,
          integrationDomain: 'tradfri',
          deviceId,
          platform: 'switch',
          name: this.indexedEntityName(this.deviceName(device, deviceResourceId), socketIndex),
          state: this.booleanState(this.value(socket, ['state', 'on', ATTR_DEVICE_STATE])) ? 'on' : 'off',
          attributes: {
            outlet: true,
            tradfriResourceType: 'device',
            tradfriResourceId: deviceResourceId,
            tradfriControl: 'socket',
            tradfriIndex: socketIndex,
          },
          available: reachable,
        });
      }

      for (const [blindIndex, blind] of this.blindControls(device).entries()) {
        const position = this.coverPosition(blind);
        entities.push({
          id: `cover.${this.entitySlug(deviceSlug, blindIndex)}`,
          uniqueId: `tradfri_cover_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_${blindIndex}`,
          integrationDomain: 'tradfri',
          deviceId,
          platform: 'cover',
          name: this.indexedEntityName(this.deviceName(device, deviceResourceId), blindIndex),
          state: position,
          attributes: {
            position,
            rawPosition: 100 - position,
            isClosed: position === 0,
            model: this.stringValue(deviceInfo.modelNumber || deviceInfo.model_number),
            tradfriResourceType: 'device',
            tradfriResourceId: deviceResourceId,
            tradfriControl: 'blind',
            tradfriIndex: blindIndex,
          },
          available: reachable,
        });
      }

      this.pushBatteryEntity(entities, snapshotArg, device, deviceInfo, deviceId, deviceSlug, reachable);

      for (const [fanIndex, fan] of this.airPurifierControls(device).entries()) {
        const fanSlug = this.entitySlug(deviceSlug, fanIndex);
        const percentage = this.fanPercentage(fan);
        entities.push({
          id: `fan.${fanSlug}`,
          uniqueId: `tradfri_fan_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_${fanIndex}`,
          integrationDomain: 'tradfri',
          deviceId,
          platform: 'fan',
          name: this.indexedEntityName(this.deviceName(device, deviceResourceId), fanIndex),
          state: this.airPurifierOn(fan) ? 'on' : 'off',
          attributes: {
            percentage,
            presetMode: this.airPurifierAuto(fan) ? 'Auto' : undefined,
            airQuality: this.airQuality(fan),
            tradfriResourceType: 'device',
            tradfriResourceId: deviceResourceId,
            tradfriControl: 'air_purifier',
            tradfriIndex: fanIndex,
          },
          available: reachable,
        });
        this.pushSensorEntity(entities, deviceId, `tradfri_sensor_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_air_quality_${fanIndex}`, `sensor.${fanSlug}_air_quality`, `${this.indexedEntityName(this.deviceName(device, deviceResourceId), fanIndex)} air quality`, this.airQuality(fan), reachable, { unit: 'ug/m3', deviceClass: 'pm25' });
        this.pushSensorEntity(entities, deviceId, `tradfri_sensor_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_filter_life_${fanIndex}`, `sensor.${fanSlug}_filter_life_remaining`, `${this.indexedEntityName(this.deviceName(device, deviceResourceId), fanIndex)} filter life remaining`, this.numberValue(this.value(fan, ['filterLifetimeRemaining', 'filter_lifetime_remaining', ATTR_AIR_PURIFIER_FILTER_LIFETIME_REMAINING])), reachable, { unit: 'min' });
      }

      for (const [sensorIndex, sensor] of this.sensorControls(device).entries()) {
        const sensorValue = this.sensorValue(sensor);
        const binary = this.binarySensor(sensor);
        const platform = binary ? 'binary_sensor' : 'sensor';
        const sensorSlug = `${deviceSlug}_${this.slug(this.sensorName(sensor, sensorIndex))}`;
        entities.push({
          id: `${platform}.${sensorSlug}`,
          uniqueId: `tradfri_${platform}_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_${sensorIndex}`,
          integrationDomain: 'tradfri',
          deviceId,
          platform,
          name: this.sensorName(sensor, sensorIndex),
          state: binary ? this.binaryState(sensorValue) : sensorValue ?? 'unknown',
          attributes: {
            unit: this.sensorUnit(sensor),
            deviceClass: this.sensorDeviceClass(sensor),
            tradfriResourceType: 'device',
            tradfriResourceId: deviceResourceId,
            tradfriControl: binary ? 'binary_sensor' : 'sensor',
            tradfriIndex: sensorIndex,
          },
          available: reachable,
        });
      }
    }

    for (const [index, group] of snapshotArg.groups.entries()) {
      const groupResourceId = this.groupResourceId(group, index);
      const groupSlug = this.slug(this.groupName(group, groupResourceId));
      entities.push({
        id: `light.${groupSlug}`,
        uniqueId: `tradfri_group_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(groupResourceId)}`,
        integrationDomain: 'tradfri',
        deviceId: this.groupDeviceId(snapshotArg, group, index),
        platform: 'light',
        name: this.groupName(group, groupResourceId),
        state: this.booleanState(this.value(group, ['state', ATTR_DEVICE_STATE])) ? 'on' : 'off',
        attributes: {
          brightness: this.numberValue(this.value(group, ['dimmer', 'brightness', ATTR_LIGHT_DIMMER])),
          brightnessPercent: this.groupBrightnessPercent(group),
          memberIds: this.groupMemberIds(group),
          tradfriResourceType: 'group',
          tradfriResourceId: groupResourceId,
          tradfriControl: 'group',
          tradfriIndex: index,
        },
        available: snapshotArg.connected ?? true,
      });
    }

    return entities;
  }

  public static commandForService(snapshotArg: ITradfriSnapshot, requestArg: IServiceCallRequest): ITradfriCommand | undefined {
    const target = this.findTargetEntity(snapshotArg, requestArg);
    if (!target || !this.serviceMatchesEntity(target, requestArg)) {
      return undefined;
    }
    const payload = this.payloadForService(target, requestArg);
    if (!payload) {
      return undefined;
    }
    const resourceType = target.attributes?.tradfriResourceType === 'group' ? 'group' : 'device';
    const resourceId = this.stringValue(target.attributes?.tradfriResourceId);
    if (!resourceId) {
      return undefined;
    }
    return this.commandFromPayload(resourceType, resourceId, target, requestArg, payload);
  }

  private static gatewayDevice(snapshotArg: ITradfriSnapshot, gatewayArg: ITradfriGateway, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    const gatewayId = this.gatewayIdentifier(snapshotArg);
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
      { id: 'device_count', capability: 'sensor', name: 'Device count', readable: true, writable: false },
      { id: 'group_count', capability: 'sensor', name: 'Group count', readable: true, writable: false },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'connectivity', value: snapshotArg.connected ? 'online' : 'configured', updatedAt: updatedAtArg },
      { featureId: 'device_count', value: snapshotArg.devices.length, updatedAt: updatedAtArg },
      { featureId: 'group_count', value: snapshotArg.groups.length, updatedAt: updatedAtArg },
    ];
    const firmware = this.stringValue(gatewayArg.firmwareVersion || gatewayArg.firmware_version || this.value(gatewayArg, ['9029']));
    if (firmware) {
      features.push({ id: 'firmware', capability: 'sensor', name: 'Firmware', readable: true, writable: false });
      state.push({ featureId: 'firmware', value: firmware, updatedAt: updatedAtArg });
    }
    return {
      id: `tradfri.gateway.${this.slug(gatewayId)}`,
      integrationDomain: 'tradfri',
      name: this.stringValue(gatewayArg.name) || 'IKEA TRADFRI Gateway',
      protocol: 'zigbee',
      manufacturer: 'IKEA of Sweden',
      model: this.stringValue(gatewayArg.model) || 'E1526',
      online: snapshotArg.connected ?? Boolean(snapshotArg.devices.length || snapshotArg.groups.length),
      features,
      state,
      metadata: {
        gatewayId,
        host: snapshotArg.host,
        port: snapshotArg.port,
        homekitId: gatewayArg.homekitId || gatewayArg.homekit_id,
        transport: 'coap-dtls',
      },
    };
  }

  private static pushBatteryEntity(entitiesArg: IIntegrationEntity[], snapshotArg: ITradfriSnapshot, deviceArg: ITradfriDevice, deviceInfoArg: ITradfriDeviceInfo, deviceIdArg: string, deviceSlugArg: string, reachableArg: boolean): void {
    const battery = this.numberValue(deviceInfoArg.batteryLevel ?? deviceInfoArg.battery_level);
    if (battery === undefined) {
      return;
    }
    const deviceResourceId = this.deviceResourceId(deviceArg);
    this.pushSensorEntity(
      entitiesArg,
      deviceIdArg,
      `tradfri_sensor_${this.slug(this.gatewayIdentifier(snapshotArg))}_${this.slug(deviceResourceId)}_battery`,
      `sensor.${deviceSlugArg}_battery`,
      `${this.deviceName(deviceArg, deviceResourceId)} battery`,
      battery,
      reachableArg,
      { unit: '%', deviceClass: 'battery', tradfriResourceType: 'device', tradfriResourceId: deviceResourceId, tradfriControl: 'battery' }
    );
  }

  private static pushSensorEntity(entitiesArg: IIntegrationEntity[], deviceIdArg: string, uniqueIdArg: string, idArg: string, nameArg: string, valueArg: unknown, availableArg: boolean, attributesArg: Record<string, unknown>): void {
    if (valueArg === undefined || valueArg === null) {
      return;
    }
    entitiesArg.push({
      id: idArg,
      uniqueId: uniqueIdArg,
      integrationDomain: 'tradfri',
      deviceId: deviceIdArg,
      platform: 'sensor',
      name: nameArg,
      state: valueArg,
      attributes: attributesArg,
      available: availableArg,
    });
  }

  private static pushOptionalFeature(featuresArg: plugins.shxInterfaces.data.IDeviceFeature[], stateArg: plugins.shxInterfaces.data.IDeviceState[], idArg: string, nameArg: string, valueArg: unknown, unitArg: string | undefined, updatedAtArg: string, capabilityArg: plugins.shxInterfaces.data.TDeviceCapability = 'sensor', writableArg = false): void {
    if (valueArg === undefined || valueArg === null) {
      return;
    }
    featuresArg.push({ id: idArg, capability: capabilityArg, name: nameArg, readable: true, writable: writableArg, unit: unitArg });
    stateArg.push({ featureId: idArg, value: this.deviceStateValue(valueArg), updatedAt: updatedAtArg });
  }

  private static findTargetEntity(snapshotArg: ITradfriSnapshot, requestArg: IServiceCallRequest): IIntegrationEntity | undefined {
    const entities = this.toEntities(snapshotArg);
    if (requestArg.target.entityId) {
      return entities.find((entityArg) => entityArg.id === requestArg.target.entityId || entityArg.uniqueId === requestArg.target.entityId);
    }
    const candidates = requestArg.target.deviceId ? entities.filter((entityArg) => entityArg.deviceId === requestArg.target.deviceId) : entities;
    return candidates.find((entityArg) => this.serviceMatchesEntity(entityArg, requestArg));
  }

  private static serviceMatchesEntity(entityArg: IIntegrationEntity, requestArg: IServiceCallRequest): boolean {
    if (requestArg.domain !== 'tradfri' && requestArg.domain !== entityArg.platform) {
      return false;
    }
    if (requestArg.service === 'turn_on' || requestArg.service === 'turn_off') {
      return ['light', 'switch', 'fan'].includes(entityArg.platform);
    }
    if (requestArg.service === 'set_position' || requestArg.service === 'open_cover' || requestArg.service === 'close_cover') {
      return entityArg.platform === 'cover';
    }
    if (requestArg.service === 'set_percentage') {
      return entityArg.platform === 'fan' || entityArg.platform === 'light';
    }
    if (requestArg.service === 'set_value') {
      return ['light', 'switch', 'cover', 'fan'].includes(entityArg.platform);
    }
    return false;
  }

  private static payloadForService(entityArg: IIntegrationEntity, requestArg: IServiceCallRequest): Record<string, unknown> | undefined {
    if (requestArg.service === 'turn_on') {
      const brightness = this.brightnessPayload(requestArg);
      if (entityArg.platform === 'fan') {
        return { state: true, percentage: this.numberValue(requestArg.data?.percentage) ?? this.numberValue(entityArg.attributes?.percentage) ?? 100 };
      }
      return { state: true, ...brightness };
    }
    if (requestArg.service === 'turn_off') {
      return { state: false };
    }
    if (requestArg.service === 'set_position') {
      const position = this.numberValue(requestArg.data?.position ?? requestArg.data?.positionPercentage ?? requestArg.data?.position_percentage);
      return position === undefined ? undefined : { position: this.clampPercent(position), rawPosition: 100 - this.clampPercent(position) };
    }
    if (requestArg.service === 'open_cover') {
      return { position: 100, rawPosition: 0 };
    }
    if (requestArg.service === 'close_cover') {
      return { position: 0, rawPosition: 100 };
    }
    if (requestArg.service === 'set_percentage') {
      const percentage = this.numberValue(requestArg.data?.percentage);
      if (percentage === undefined) {
        return undefined;
      }
      if (entityArg.platform === 'light') {
        return { brightnessPercent: this.clampPercent(percentage), brightness: this.percentToBrightness(percentage), state: percentage > 0 };
      }
      return { percentage: this.clampPercent(percentage), mode: this.percentageToFanMode(percentage), state: percentage > 0 };
    }
    if (requestArg.service === 'set_value') {
      const value = requestArg.data?.value;
      if (value === undefined) {
        return undefined;
      }
      if (entityArg.platform === 'cover' && typeof value === 'number') {
        const position = this.clampPercent(value);
        return { value, position, rawPosition: 100 - position };
      }
      if (entityArg.platform === 'fan' && typeof value === 'number') {
        return { value, percentage: this.clampPercent(value), mode: this.percentageToFanMode(value), state: value > 0 };
      }
      if ((entityArg.platform === 'light' || entityArg.platform === 'switch') && typeof value === 'boolean') {
        return { value, state: value };
      }
      return { value };
    }
    return undefined;
  }

  private static commandFromPayload(resourceTypeArg: Exclude<TTradfriResourceType, 'gateway'>, resourceIdArg: string, entityArg: IIntegrationEntity, requestArg: IServiceCallRequest, payloadArg: Record<string, unknown>): ITradfriCommand {
    const coapPayload = this.coapPayload(resourceTypeArg, entityArg.platform, payloadArg);
    return {
      type: `${entityArg.platform}.command`,
      service: requestArg.service,
      resourceType: resourceTypeArg,
      resourceId: resourceIdArg,
      platform: entityArg.platform,
      deviceId: entityArg.deviceId,
      entityId: entityArg.id,
      uniqueId: entityArg.uniqueId,
      payload: payloadArg,
      coap: {
        method: 'put',
        path: [resourceTypeArg === 'group' ? ROOT_GROUPS : ROOT_DEVICES, resourceIdArg],
        payload: coapPayload,
      },
      target: requestArg.target,
    };
  }

  private static coapPayload(resourceTypeArg: Exclude<TTradfriResourceType, 'gateway'>, platformArg: TEntityPlatform, payloadArg: Record<string, unknown>): Record<string, unknown> {
    const values: Record<string, unknown> = {};
    if (typeof payloadArg.state === 'boolean') {
      values[ATTR_DEVICE_STATE] = payloadArg.state ? 1 : 0;
    }
    const brightness = this.numberValue(payloadArg.brightness);
    if (brightness !== undefined) {
      values[ATTR_LIGHT_DIMMER] = this.clamp(brightness, 0, 254);
    }
    const rawPosition = this.numberValue(payloadArg.rawPosition);
    if (platformArg === 'cover' && rawPosition !== undefined) {
      return { [ATTR_START_BLINDS]: [{ [ATTR_BLIND_CURRENT_POSITION]: this.clamp(rawPosition, 0, 100) }] };
    }
    const mode = this.numberValue(payloadArg.mode);
    if (platformArg === 'fan') {
      return { [ROOT_AIR_PURIFIER]: [{ [ATTR_AIR_PURIFIER_MODE]: mode ?? (payloadArg.state === false ? 0 : 1) }] };
    }
    if (resourceTypeArg === 'group') {
      return values;
    }
    if (platformArg === 'switch') {
      return { [ATTR_SWITCH_PLUG]: [values] };
    }
    return { [ATTR_LIGHT_CONTROL]: [values] };
  }

  private static gateway(snapshotArg: ITradfriSnapshot): ITradfriGateway {
    return snapshotArg.gateway || { id: snapshotArg.host || 'configured', name: 'IKEA TRADFRI Gateway', model: 'E1526' };
  }

  private static gatewayIdentifier(snapshotArg: ITradfriSnapshot): string {
    const gateway = this.gateway(snapshotArg);
    return this.stringValue(gateway.id || this.value(gateway, ['9081']) || snapshotArg.host) || 'configured';
  }

  private static deviceId(snapshotArg: ITradfriSnapshot, deviceArg: ITradfriDevice, fallbackIndexArg = 0): string {
    return `tradfri.device.${this.slug(this.gatewayIdentifier(snapshotArg))}.${this.slug(this.deviceResourceId(deviceArg, fallbackIndexArg))}`;
  }

  private static groupDeviceId(snapshotArg: ITradfriSnapshot, groupArg: ITradfriGroup, fallbackIndexArg = 0): string {
    return `tradfri.group.${this.slug(this.gatewayIdentifier(snapshotArg))}.${this.slug(this.groupResourceId(groupArg, fallbackIndexArg))}`;
  }

  private static deviceResourceId(deviceArg: ITradfriDevice, fallbackIndexArg = 0): string {
    return this.stringValue(this.value(deviceArg, ['id', ATTR_ID])) || `device_${fallbackIndexArg}`;
  }

  private static groupResourceId(groupArg: ITradfriGroup, fallbackIndexArg = 0): string {
    return this.stringValue(this.value(groupArg, ['id', ATTR_ID])) || `group_${fallbackIndexArg}`;
  }

  private static deviceName(deviceArg: ITradfriDevice, fallbackArg: string): string {
    return this.stringValue(this.value(deviceArg, ['name', ATTR_NAME])) || `IKEA TRADFRI ${fallbackArg}`;
  }

  private static groupName(groupArg: ITradfriGroup, fallbackArg: string): string {
    return this.stringValue(this.value(groupArg, ['name', ATTR_NAME])) || `IKEA TRADFRI Group ${fallbackArg}`;
  }

  private static deviceInfo(deviceArg: ITradfriDevice): ITradfriDeviceInfo {
    const rawInfo = this.value(deviceArg, ['deviceInfo', 'device_info', ATTR_DEVICE_INFO]);
    const info = this.isRecord(rawInfo) ? rawInfo : {};
    return {
      ...info,
      manufacturer: this.stringValue(info.manufacturer ?? info[ATTR_DEVICE_MANUFACTURER]),
      modelNumber: this.stringValue(info.modelNumber ?? info.model_number ?? info[ATTR_DEVICE_MODEL_NUMBER]),
      serial: this.stringValue(info.serial ?? info[ATTR_DEVICE_SERIAL]),
      firmwareVersion: this.stringValue(info.firmwareVersion ?? info.firmware_version ?? info[ATTR_DEVICE_FIRMWARE_VERSION]),
      powerSource: this.numberValue(info.powerSource ?? info.power_source ?? info['6']),
      batteryLevel: this.numberValue(info.batteryLevel ?? info.battery_level ?? info[ATTR_DEVICE_BATTERY]),
      raw: info,
    };
  }

  private static reachable(deviceArg: ITradfriDevice): boolean {
    const value = this.value(deviceArg, ['reachable', ATTR_REACHABLE_STATE]);
    if (typeof value === 'boolean') {
      return value;
    }
    if (typeof value === 'number') {
      return value === 1;
    }
    return true;
  }

  private static lightControls(deviceArg: ITradfriDevice): ITradfriLight[] {
    return this.arrayValue<ITradfriLight>(this.value(deviceArg, ['lightControl', 'light_control', ATTR_LIGHT_CONTROL]));
  }

  private static socketControls(deviceArg: ITradfriDevice): ITradfriSocket[] {
    return this.arrayValue<ITradfriSocket>(this.value(deviceArg, ['socketControl', 'socket_control', 'outletControl', 'outlet_control', ATTR_SWITCH_PLUG]));
  }

  private static blindControls(deviceArg: ITradfriDevice): ITradfriBlind[] {
    return this.arrayValue<ITradfriBlind>(this.value(deviceArg, ['blindControl', 'blind_control', 'coverControl', 'cover_control', ATTR_START_BLINDS]));
  }

  private static airPurifierControls(deviceArg: ITradfriDevice): ITradfriAirPurifier[] {
    return this.arrayValue<ITradfriAirPurifier>(this.value(deviceArg, ['airPurifierControl', 'air_purifier_control', ROOT_AIR_PURIFIER]));
  }

  private static sensorControls(deviceArg: ITradfriDevice): ITradfriSensor[] {
    return [
      ...this.arrayValue<ITradfriSensor>(this.value(deviceArg, ['sensorControl', 'sensor_control', 'sensors', ATTR_SENSOR])),
      ...this.arrayValue<ITradfriSensor>(this.value(deviceArg, ['binarySensors', 'binary_sensors'])),
    ];
  }

  private static lightBrightnessPercent(lightArg: ITradfriLight): number | undefined {
    const percent = this.numberValue(this.value(lightArg, ['brightnessPercent', 'brightness_percent']));
    if (percent !== undefined) {
      return this.clampPercent(percent);
    }
    const raw = this.numberValue(this.value(lightArg, ['dimmer', 'brightness', ATTR_LIGHT_DIMMER]));
    return raw === undefined ? undefined : Math.round(this.clamp(raw, 0, 254) / 254 * 100);
  }

  private static groupBrightnessPercent(groupArg: ITradfriGroup): number | undefined {
    const raw = this.numberValue(this.value(groupArg, ['dimmer', 'brightness', ATTR_LIGHT_DIMMER]));
    return raw === undefined ? undefined : Math.round(this.clamp(raw, 0, 254) / 254 * 100);
  }

  private static coverPosition(blindArg: ITradfriBlind): number {
    const position = this.numberValue(this.value(blindArg, ['position', 'positionPercentage', 'position_percentage']));
    if (position !== undefined) {
      return this.clampPercent(position);
    }
    const rawPosition = this.numberValue(this.value(blindArg, ['currentCoverPosition', 'current_cover_position', 'rawPosition', 'raw_position', ATTR_BLIND_CURRENT_POSITION]));
    return rawPosition === undefined ? 0 : this.clampPercent(100 - rawPosition);
  }

  private static airPurifierOn(fanArg: ITradfriAirPurifier): boolean {
    const state = this.value(fanArg, ['state']);
    if (typeof state === 'boolean') {
      return state;
    }
    const mode = this.numberValue(this.value(fanArg, ['mode', ATTR_AIR_PURIFIER_MODE]));
    return mode !== undefined ? mode > 0 : this.fanPercentage(fanArg) > 0;
  }

  private static airPurifierAuto(fanArg: ITradfriAirPurifier): boolean {
    return this.numberValue(this.value(fanArg, ['mode', ATTR_AIR_PURIFIER_MODE])) === 1;
  }

  private static fanPercentage(fanArg: ITradfriAirPurifier): number {
    const percentage = this.numberValue(this.value(fanArg, ['percentage']));
    if (percentage !== undefined) {
      return this.clampPercent(percentage);
    }
    const speed = this.numberValue(this.value(fanArg, ['fanSpeed', 'fan_speed', ATTR_AIR_PURIFIER_FAN_SPEED]));
    if (speed === undefined) {
      return this.airPurifierOn(fanArg) ? 100 : 0;
    }
    return Math.max(Math.round((speed - 1) / 49 * 100), 0);
  }

  private static airQuality(fanArg: ITradfriAirPurifier): number | undefined {
    const value = this.numberValue(this.value(fanArg, ['airQuality', 'air_quality', ATTR_AIR_PURIFIER_AIR_QUALITY]));
    return value === 65535 ? undefined : value;
  }

  private static sensorName(sensorArg: ITradfriSensor, indexArg: number): string {
    return this.stringValue(this.value(sensorArg, ['name', 'type', 'deviceClass', 'device_class', ATTR_SENSOR_TYPE])) || `Sensor ${indexArg}`;
  }

  private static sensorValue(sensorArg: ITradfriSensor): unknown {
    return this.value(sensorArg, ['value', 'state', ATTR_SENSOR_VALUE]);
  }

  private static sensorUnit(sensorArg: ITradfriSensor): string | undefined {
    return this.stringValue(this.value(sensorArg, ['unit', 'unitOfMeasurement', 'unit_of_measurement', ATTR_SENSOR_UNIT]));
  }

  private static sensorDeviceClass(sensorArg: ITradfriSensor): string | undefined {
    return this.stringValue(this.value(sensorArg, ['deviceClass', 'device_class', 'type', ATTR_SENSOR_TYPE]));
  }

  private static binarySensor(sensorArg: ITradfriSensor): boolean {
    if (sensorArg.binary === true) {
      return true;
    }
    const type = `${this.sensorDeviceClass(sensorArg) || ''} ${this.sensorName(sensorArg, 0)}`.toLowerCase();
    if (type.includes('motion') || type.includes('presence') || type.includes('occupancy') || type.includes('contact') || type.includes('open')) {
      return true;
    }
    return typeof this.sensorValue(sensorArg) === 'boolean';
  }

  private static binaryState(valueArg: unknown): 'on' | 'off' {
    if (typeof valueArg === 'string') {
      return ['on', 'open', 'true', '1', 'detected'].includes(valueArg.toLowerCase()) ? 'on' : 'off';
    }
    return valueArg === true || valueArg === 1 ? 'on' : 'off';
  }

  private static groupMemberIds(groupArg: ITradfriGroup): Array<string | number> {
    const direct = this.value(groupArg, ['memberIds', 'member_ids']);
    if (Array.isArray(direct)) {
      return direct.filter((valueArg) => typeof valueArg === 'string' || typeof valueArg === 'number');
    }
    const members = this.value(groupArg, ['groupMembers', 'group_members', ATTR_GROUP_MEMBERS]);
    if (this.isRecord(members)) {
      const homeSmartLink = members[ATTR_HS_LINK];
      if (this.isRecord(homeSmartLink) && Array.isArray(homeSmartLink[ATTR_ID])) {
        return homeSmartLink[ATTR_ID].filter((valueArg) => typeof valueArg === 'string' || typeof valueArg === 'number');
      }
    }
    return [];
  }

  private static brightnessPayload(requestArg: IServiceCallRequest): Record<string, unknown> {
    const rawBrightness = this.numberValue(requestArg.data?.brightness);
    if (rawBrightness !== undefined) {
      return { brightness: this.clamp(rawBrightness, 0, 254), brightnessPercent: Math.round(this.clamp(rawBrightness, 0, 254) / 254 * 100) };
    }
    const percent = this.numberValue(requestArg.data?.brightnessPct ?? requestArg.data?.brightness_pct ?? requestArg.data?.percentage);
    if (percent !== undefined) {
      return { brightness: this.percentToBrightness(percent), brightnessPercent: this.clampPercent(percent) };
    }
    return {};
  }

  private static percentageToFanMode(valueArg: number): number {
    const percentage = this.clampPercent(valueArg);
    if (percentage <= 0) {
      return 0;
    }
    return Math.round(Math.max(2, (percentage / 100 * 49) + 1));
  }

  private static percentToBrightness(valueArg: number): number {
    return Math.round(this.clampPercent(valueArg) / 100 * 254);
  }

  private static indexedFeatureId(prefixArg: string, indexArg: number): string {
    return indexArg === 0 ? prefixArg : `${prefixArg}_${indexArg}`;
  }

  private static indexedName(nameArg: string, indexArg: number): string {
    return indexArg === 0 ? nameArg : `${nameArg} ${indexArg + 1}`;
  }

  private static indexedEntityName(nameArg: string, indexArg: number): string {
    return indexArg === 0 ? nameArg : `${nameArg} ${indexArg + 1}`;
  }

  private static entitySlug(deviceSlugArg: string, indexArg: number): string {
    return indexArg === 0 ? deviceSlugArg : `${deviceSlugArg}_${indexArg + 1}`;
  }

  private static booleanState(valueArg: unknown): boolean {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (typeof valueArg === 'number') {
      return valueArg === 1;
    }
    if (typeof valueArg === 'string') {
      return ['1', 'true', 'on'].includes(valueArg.toLowerCase());
    }
    return false;
  }

  private static value(sourceArg: unknown, keysArg: string[]): unknown {
    if (!this.isRecord(sourceArg)) {
      return undefined;
    }
    for (const key of keysArg) {
      if (sourceArg[key] !== undefined) {
        return sourceArg[key];
      }
    }
    return undefined;
  }

  private static arrayValue<TValue>(valueArg: unknown): TValue[] {
    if (!valueArg) {
      return [];
    }
    if (Array.isArray(valueArg)) {
      return valueArg as TValue[];
    }
    if (this.isRecord(valueArg)) {
      return [valueArg as TValue];
    }
    return [];
  }

  private static stringValue(valueArg: unknown): string | undefined {
    if (typeof valueArg === 'string' && valueArg) {
      return valueArg;
    }
    if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
      return String(valueArg);
    }
    return undefined;
  }

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

  private static clampPercent(valueArg: number): number {
    return this.clamp(valueArg, 0, 100);
  }

  private static clamp(valueArg: number, minArg: number, maxArg: number): number {
    return Math.max(minArg, Math.min(maxArg, valueArg));
  }

  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 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, '') || 'tradfri';
  }
}
