import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest } from '../../core/types.js';
import type {
  IBoschShcBinarySensorFeature,
  IBoschShcClimateFeature,
  IBoschShcCoverFeature,
  IBoschShcDevice,
  IBoschShcDeviceService,
  IBoschShcFeatureBase,
  IBoschShcLightFeature,
  IBoschShcModeledCommand,
  IBoschShcSensorFeature,
  IBoschShcSnapshot,
  IBoschShcSwitchFeature,
  TBoschShcCoverState,
  TBoschShcFeature,
} from './bosch_shc.types.js';

const thermostatModels = new Set(['TRV', 'TRV_GEN2', 'TRV_GEN2_DUAL']);
const wallThermostatModels = new Set(['THB', 'BWTH', 'BWTH24', 'RTH2_BAT', 'RTH2_230']);
const shutterContactModels = new Set(['SWD', 'SWD2', 'SWD2_PLUS', 'SWD2_DUAL']);
const batteryModels = new Set([
  'MD',
  'SWD',
  'SWD2',
  'SWD2_PLUS',
  'SWD2_DUAL',
  'SD',
  'SMOKE_DETECTOR2',
  'TRV',
  'TRV_GEN2',
  'TRV_GEN2_DUAL',
  'TWINGUARD',
  'WRC2',
  'SWITCH2',
  'THB',
  'BWTH',
  'BWTH24',
  'RTH2_BAT',
  'RTH2_230',
  'WLS',
]);
const smartPlugModels = new Set(['PSM', 'PLUG_COMPACT', 'PLUG_COMPACT_DUAL']);
const lightSwitchModels = new Set(['BSM', 'MICROMODULE_LIGHT_ATTACHED', 'MICROMODULE_RELAY']);
const lightModels = new Set(['LEDVANCE_LIGHT', 'HUE_LIGHT', 'MICROMODULE_DIMMER']);

const sensorMetadata: Record<string, { name: string; unit?: string; deviceClass?: string; stateClass?: string }> = {
  temperature: { name: 'Temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  humidity: { name: 'Humidity', unit: '%', deviceClass: 'humidity' },
  valvetappet: { name: 'Valvetappet', unit: '%', stateClass: 'measurement' },
  purity: { name: 'Purity', unit: 'ppm' },
  airquality: { name: 'Air quality' },
  temperature_rating: { name: 'Temperature rating' },
  humidity_rating: { name: 'Humidity rating' },
  purity_rating: { name: 'Purity rating' },
  power: { name: 'Power', unit: 'W', deviceClass: 'power' },
  energy: { name: 'Energy', unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' },
  communication_quality: { name: 'Communication quality' },
};

export class BoschShcMapper {
  public static toDevices(snapshotArg: IBoschShcSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const controllerId = this.controllerDeviceId(snapshotArg);
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [{
      id: controllerId,
      integrationDomain: 'bosch_shc',
      name: snapshotArg.name || snapshotArg.information?.shcIpAddress || 'Bosch Smart Home Controller',
      protocol: 'http',
      manufacturer: 'Bosch',
      model: 'SmartHomeController',
      online: snapshotArg.online !== false,
      features: [
        { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
      ],
      state: [
        { featureId: 'connectivity', value: snapshotArg.online === false ? 'offline' : 'online', updatedAt },
      ],
      metadata: {
        host: snapshotArg.host || snapshotArg.information?.shcIpAddress,
        uniqueId: this.controllerUniqueId(snapshotArg),
        macAddress: snapshotArg.information?.macAddress,
        softwareVersion: snapshotArg.information?.softwareUpdateState?.swInstalledVersion,
        updateState: snapshotArg.information?.softwareUpdateState?.swUpdateState,
      },
    }];

    const featuresByDevice = new Map<string, TBoschShcFeature[]>();
    for (const feature of this.features(snapshotArg)) {
      const entries = featuresByDevice.get(feature.deviceId) || [];
      entries.push(feature);
      featuresByDevice.set(feature.deviceId, entries);
    }

    for (const device of snapshotArg.devices) {
      if (device.deleted) continue;
      const featureEntries = featuresByDevice.get(device.id) || [];
      const shxFeatures: plugins.shxInterfaces.data.IDeviceFeature[] = [
        { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
      ];
      const state: plugins.shxInterfaces.data.IDeviceState[] = [
        { featureId: 'connectivity', value: this.available(device) ? 'online' : 'offline', updatedAt },
      ];

      for (const feature of featureEntries) {
        if (feature.platform === 'sensor') {
          shxFeatures.push({ id: feature.id, capability: 'sensor', name: feature.name, readable: true, writable: false, unit: feature.unit });
          state.push({ featureId: feature.id, value: feature.nativeValue, updatedAt });
        } else if (feature.platform === 'binary_sensor') {
          shxFeatures.push({ id: feature.id, capability: 'sensor', name: feature.name, readable: true, writable: false });
          state.push({ featureId: feature.id, value: feature.isOn, updatedAt });
        } else if (feature.platform === 'switch') {
          shxFeatures.push({ id: feature.id, capability: 'switch', name: feature.name, readable: true, writable: true });
          state.push({ featureId: feature.id, value: feature.isOn, updatedAt });
        } else if (feature.platform === 'light') {
          shxFeatures.push({ id: feature.id, capability: 'light', name: feature.name, readable: true, writable: true });
          state.push({ featureId: feature.id, value: feature.isOn, updatedAt });
          if (feature.supportsBrightness) {
            shxFeatures.push({ id: `${feature.id}_brightness`, capability: 'light', name: `${feature.name} brightness`, readable: true, writable: true, unit: '%' });
            state.push({ featureId: `${feature.id}_brightness`, value: feature.brightness ?? null, updatedAt });
          }
        } else if (feature.platform === 'cover') {
          shxFeatures.push({ id: feature.id, capability: 'cover', name: feature.name, readable: true, writable: true, unit: '%' });
          state.push({ featureId: feature.id, value: feature.state, updatedAt });
          if (feature.position !== null) {
            shxFeatures.push({ id: `${feature.id}_position`, capability: 'cover', name: `${feature.name} position`, readable: true, writable: true, unit: '%' });
            state.push({ featureId: `${feature.id}_position`, value: feature.position, updatedAt });
          }
        } else if (feature.platform === 'climate') {
          shxFeatures.push({ id: feature.id, capability: 'climate', name: feature.name, readable: true, writable: true });
          state.push({ featureId: feature.id, value: feature.hvacMode, updatedAt });
          if (feature.targetTemperature !== undefined) {
            shxFeatures.push({ id: `${feature.id}_target_temperature`, capability: 'climate', name: `${feature.name} target temperature`, readable: true, writable: true, unit: 'C' });
            state.push({ featureId: `${feature.id}_target_temperature`, value: feature.targetTemperature, updatedAt });
          }
          if (feature.currentTemperature !== undefined) {
            shxFeatures.push({ id: `${feature.id}_current_temperature`, capability: 'climate', name: `${feature.name} current temperature`, readable: true, writable: false, unit: 'C' });
            state.push({ featureId: `${feature.id}_current_temperature`, value: feature.currentTemperature, updatedAt });
          }
        }
      }

      devices.push({
        id: this.deviceId(device),
        integrationDomain: 'bosch_shc',
        name: device.name || device.id,
        protocol: 'http',
        manufacturer: this.manufacturer(device),
        model: device.deviceModel || 'Bosch SHC device',
        online: this.available(device),
        features: shxFeatures,
        state,
        metadata: {
          rootDeviceId: device.rootDeviceId,
          serial: device.serial,
          profile: device.profile,
          roomId: device.roomId,
          status: device.status,
          viaDevice: controllerId,
        },
      });
    }

    return devices;
  }

  public static toEntities(snapshotArg: IBoschShcSnapshot): IIntegrationEntity[] {
    return this.features(snapshotArg).map((featureArg) => {
      const base = {
        id: this.entityId(featureArg),
        uniqueId: featureArg.uniqueId,
        integrationDomain: 'bosch_shc',
        deviceId: this.deviceId(featureArg.device),
        platform: featureArg.platform,
        name: featureArg.name,
        available: featureArg.available,
      };

      if (featureArg.platform === 'sensor') {
        return {
          ...base,
          state: featureArg.nativeValue,
          attributes: {
            unit: featureArg.unit,
            deviceClass: featureArg.deviceClass,
            stateClass: featureArg.stateClass,
            ...featureArg.attributes,
          },
        };
      }
      if (featureArg.platform === 'binary_sensor') {
        return {
          ...base,
          state: featureArg.isOn ? 'on' : 'off',
          attributes: {
            deviceClass: featureArg.deviceClass,
            ...featureArg.attributes,
          },
        };
      }
      if (featureArg.platform === 'switch') {
        return {
          ...base,
          state: featureArg.isOn ? 'on' : 'off',
          attributes: {
            deviceClass: featureArg.deviceClass,
            entityCategory: featureArg.entityCategory,
            switchKind: featureArg.kind,
          },
        };
      }
      if (featureArg.platform === 'light') {
        return {
          ...base,
          state: featureArg.isOn ? 'on' : 'off',
          attributes: {
            brightness: featureArg.brightness,
            colorTemperature: featureArg.colorTemperature,
            minColorTemperature: featureArg.minColorTemperature,
            maxColorTemperature: featureArg.maxColorTemperature,
            rgb: featureArg.rgb,
            supportsBrightness: featureArg.supportsBrightness,
            supportsColorTemperature: featureArg.supportsColorTemperature,
            supportsRgb: featureArg.supportsRgb,
          },
        };
      }
      if (featureArg.platform === 'cover') {
        return {
          ...base,
          state: featureArg.state,
          attributes: {
            currentPosition: featureArg.position,
            deviceClass: featureArg.deviceClass,
            operationState: featureArg.operationState,
            calibrated: featureArg.calibrated,
            supportsOpen: featureArg.supportsOpen,
            supportsClose: featureArg.supportsClose,
            supportsStop: featureArg.supportsStop,
            supportsPosition: featureArg.supportsPosition,
          },
        };
      }
      return {
        ...base,
        state: featureArg.hvacMode,
        attributes: {
          currentTemperature: featureArg.currentTemperature,
          targetTemperature: featureArg.targetTemperature,
          operationMode: featureArg.operationMode,
          boostMode: featureArg.boostMode,
          low: featureArg.low,
          summerMode: featureArg.summerMode,
          supportsBoostMode: featureArg.supportsBoostMode,
          supportedHvacModes: featureArg.supportedHvacModes,
        },
      };
    });
  }

  public static features(snapshotArg: IBoschShcSnapshot): TBoschShcFeature[] {
    return [
      ...this.sensorFeatures(snapshotArg),
      ...this.binarySensorFeatures(snapshotArg),
      ...this.switchFeatures(snapshotArg),
      ...this.lightFeatures(snapshotArg),
      ...this.coverFeatures(snapshotArg),
      ...this.climateFeatures(snapshotArg),
    ];
  }

  public static commandForService(snapshotArg: IBoschShcSnapshot, requestArg: IServiceCallRequest): IBoschShcModeledCommand | { error: string } | undefined {
    if (requestArg.domain === 'switch') {
      return this.switchCommand(snapshotArg, requestArg);
    }
    if (requestArg.domain === 'light') {
      return this.lightCommand(snapshotArg, requestArg);
    }
    if (requestArg.domain === 'cover') {
      return this.coverCommand(snapshotArg, requestArg);
    }
    if (requestArg.domain === 'climate') {
      return this.climateCommand(snapshotArg, requestArg);
    }
    return undefined;
  }

  public static controllerDeviceId(snapshotArg: IBoschShcSnapshot): string {
    return `bosch_shc.controller.${this.slug(this.controllerUniqueId(snapshotArg) || snapshotArg.host || 'controller')}`;
  }

  public static deviceId(deviceArg: IBoschShcDevice): string {
    return `bosch_shc.device.${this.slug(deviceArg.id || deviceArg.serial || deviceArg.name || 'device')}`;
  }

  public static entityId(featureArg: TBoschShcFeature): string {
    const deviceSlug = this.slug(featureArg.device.name || featureArg.device.id || 'bosch_shc');
    const alias = this.slug(featureArg.alias);
    const objectId = this.usesDefaultEntityObjectId(featureArg) ? deviceSlug : `${deviceSlug}_${alias}`;
    return `${featureArg.platform}.${objectId}`;
  }

  private static sensorFeatures(snapshotArg: IBoschShcSnapshot): IBoschShcSensorFeature[] {
    const features: IBoschShcSensorFeature[] = [];
    for (const device of this.activeDevices(snapshotArg)) {
      const model = device.deviceModel || '';
      const temperature = this.service(snapshotArg, device, 'TemperatureLevel');
      if (temperature && (thermostatModels.has(model) || wallThermostatModels.has(model))) {
        features.push(this.sensorFeature(device, 'temperature', this.numberState(temperature, 'temperature'), temperature));
      }

      const humidity = this.service(snapshotArg, device, 'HumidityLevel');
      if (humidity && wallThermostatModels.has(model)) {
        features.push(this.sensorFeature(device, 'humidity', this.numberState(humidity, 'humidity'), humidity));
      }

      const valve = this.service(snapshotArg, device, 'ValveTappet');
      if (valve && thermostatModels.has(model)) {
        features.push(this.sensorFeature(device, 'valvetappet', this.numberState(valve, 'position'), valve, {
          valve_tappet_state: this.stringState(valve, 'value'),
        }));
      }

      const airQuality = this.service(snapshotArg, device, 'AirQualityLevel');
      if (airQuality && model === 'TWINGUARD') {
        features.push(this.sensorFeature(device, 'temperature', this.numberState(airQuality, 'temperature'), airQuality));
        features.push(this.sensorFeature(device, 'humidity', this.numberState(airQuality, 'humidity'), airQuality));
        features.push(this.sensorFeature(device, 'purity', this.numberState(airQuality, 'purity'), airQuality));
        features.push(this.sensorFeature(device, 'airquality', this.stringState(airQuality, 'combinedRating'), airQuality, {
          rating_description: this.stringState(airQuality, 'description'),
        }));
        features.push(this.sensorFeature(device, 'temperature_rating', this.stringState(airQuality, 'temperatureRating'), airQuality));
        features.push(this.sensorFeature(device, 'humidity_rating', this.stringState(airQuality, 'humidityRating'), airQuality));
        features.push(this.sensorFeature(device, 'purity_rating', this.stringState(airQuality, 'purityRating'), airQuality));
      }

      const powerMeter = this.service(snapshotArg, device, 'PowerMeter');
      if (powerMeter && (smartPlugModels.has(model) || model === 'BSM' || this.hasService(device, 'PowerSwitch'))) {
        features.push(this.sensorFeature(device, 'power', this.numberState(powerMeter, 'powerConsumption'), powerMeter));
        const energyWh = this.numberState(powerMeter, 'energyConsumption');
        features.push(this.sensorFeature(device, 'energy', typeof energyWh === 'number' ? energyWh / 1000 : null, powerMeter));
      }

      const communicationQuality = this.service(snapshotArg, device, 'CommunicationQuality');
      if (communicationQuality && smartPlugModels.has(model)) {
        features.push(this.sensorFeature(device, 'communication_quality', this.stringState(communicationQuality, 'quality'), communicationQuality));
      }
    }
    return features;
  }

  private static binarySensorFeatures(snapshotArg: IBoschShcSnapshot): IBoschShcBinarySensorFeature[] {
    const features: IBoschShcBinarySensorFeature[] = [];
    for (const device of this.activeDevices(snapshotArg)) {
      const model = device.deviceModel || '';
      const shutterContact = this.service(snapshotArg, device, 'ShutterContact');
      if (shutterContact && shutterContactModels.has(model)) {
        const profile = device.profile || '';
        features.push({
          ...this.featureBase(device, 'binary_sensor', 'contact', this.uniqueId(device, 'contact', true), device.name || 'Shutter contact'),
          isOn: this.stringState(shutterContact, 'value') === 'OPEN',
          deviceClass: profile === 'ENTRANCE_DOOR' || profile === 'FRENCH_WINDOW' ? 'door' : 'window',
          attributes: { profile },
        });
      }

      const battery = this.service(snapshotArg, device, 'BatteryLevel');
      if (battery && (batteryModels.has(model) || this.hasService(device, 'BatteryLevel'))) {
        const level = this.batteryWarningLevel(battery);
        features.push({
          ...this.featureBase(device, 'binary_sensor', 'battery', this.uniqueId(device, 'battery'), `${device.name || 'Bosch SHC device'} battery`),
          isOn: level !== 'OK',
          deviceClass: 'battery',
          attributes: { warningLevel: level },
        });
      }

      const leakage = this.service(snapshotArg, device, 'WaterLeakageSensor');
      if (leakage) {
        features.push({
          ...this.featureBase(device, 'binary_sensor', 'water_leakage', this.uniqueId(device, 'water_leakage'), `${device.name || 'Bosch SHC device'} water leakage`),
          isOn: this.stringState(leakage, 'state') === 'LEAKAGE_DETECTED',
          deviceClass: 'moisture',
        });
      }
    }
    return features;
  }

  private static switchFeatures(snapshotArg: IBoschShcSnapshot): IBoschShcSwitchFeature[] {
    const features: IBoschShcSwitchFeature[] = [];
    for (const device of this.activeDevices(snapshotArg)) {
      const model = device.deviceModel || '';
      const powerSwitch = this.service(snapshotArg, device, 'PowerSwitch');
      if (powerSwitch && !lightModels.has(model)) {
        const isOutlet = smartPlugModels.has(model);
        features.push({
          ...this.featureBase(device, 'switch', 'power', this.uniqueId(device, 'power', true), device.name || 'Bosch SHC switch'),
          kind: 'power_switch',
          isOn: this.stringState(powerSwitch, 'switchState') === 'ON',
          deviceClass: isOutlet ? 'outlet' : 'switch',
          serviceId: 'PowerSwitch',
          stateKey: 'switchState',
          onValue: 'ON',
          offValue: 'OFF',
        });
      }

      const routing = this.service(snapshotArg, device, 'Routing');
      if (routing && model === 'PSM') {
        features.push({
          ...this.featureBase(device, 'switch', 'routing', this.uniqueId(device, 'routing'), `${device.name || 'Bosch SHC device'} routing`),
          kind: 'routing',
          isOn: this.stringState(routing, 'value') === 'ENABLED',
          deviceClass: 'switch',
          serviceId: 'Routing',
          stateKey: 'value',
          onValue: 'ENABLED',
          offValue: 'DISABLED',
          entityCategory: 'config',
        });
      }

      const cameraLight = this.service(snapshotArg, device, 'CameraLight');
      if (cameraLight && model === 'CAMERA_EYES') {
        features.push({
          ...this.featureBase(device, 'switch', 'camera_light', this.uniqueId(device, 'camera_light'), `${device.name || 'Bosch SHC camera'} light`),
          kind: 'camera_light',
          isOn: this.stringState(cameraLight, 'value') === 'ON',
          deviceClass: 'switch',
          serviceId: 'CameraLight',
          stateKey: 'value',
          onValue: 'ON',
          offValue: 'OFF',
        });
      }

      const privacy = this.service(snapshotArg, device, 'PrivacyMode');
      if (privacy && model === 'CAMERA_360') {
        features.push({
          ...this.featureBase(device, 'switch', 'privacy_mode', this.uniqueId(device, 'privacy_mode'), `${device.name || 'Bosch SHC camera'} privacy mode`),
          kind: 'privacy_mode',
          isOn: this.stringState(privacy, 'value') === 'DISABLED',
          deviceClass: 'switch',
          serviceId: 'PrivacyMode',
          stateKey: 'value',
          onValue: 'DISABLED',
          offValue: 'ENABLED',
        });
      }
    }
    return features.filter((featureArg) => lightSwitchModels.has(featureArg.device.deviceModel || '') || smartPlugModels.has(featureArg.device.deviceModel || '') || featureArg.kind !== 'power_switch');
  }

  private static lightFeatures(snapshotArg: IBoschShcSnapshot): IBoschShcLightFeature[] {
    const features: IBoschShcLightFeature[] = [];
    for (const device of this.activeDevices(snapshotArg)) {
      const model = device.deviceModel || '';
      if (!lightModels.has(model) && !this.service(snapshotArg, device, 'BinarySwitch')) continue;

      const binarySwitch = this.service(snapshotArg, device, 'BinarySwitch');
      const powerSwitch = this.service(snapshotArg, device, 'PowerSwitch');
      const switchService = binarySwitch || powerSwitch;
      if (!switchService) continue;

      const brightness = this.service(snapshotArg, device, 'MultiLevelSwitch');
      const colorTemperature = this.service(snapshotArg, device, 'HueColorTemperature');
      const rgb = this.service(snapshotArg, device, 'HSBColorActuator');
      features.push({
        ...this.featureBase(device, 'light', 'light', this.uniqueId(device, 'light', true), device.name || 'Bosch SHC light'),
        isOn: binarySwitch ? this.booleanState(binarySwitch, 'on') ?? null : this.stringState(switchService, 'switchState') === 'ON',
        switchServiceId: binarySwitch ? 'BinarySwitch' : 'PowerSwitch',
        brightness: brightness ? this.numberState(brightness, 'level') : null,
        colorTemperature: colorTemperature ? this.numberState(colorTemperature, 'colorTemperature') : null,
        minColorTemperature: colorTemperature ? this.numberNestedState(colorTemperature, 'colorTemperatureRange', 'minCt') : rgb ? this.numberNestedState(rgb, 'colorTemperatureRange', 'minCt') : null,
        maxColorTemperature: colorTemperature ? this.numberNestedState(colorTemperature, 'colorTemperatureRange', 'maxCt') : rgb ? this.numberNestedState(rgb, 'colorTemperatureRange', 'maxCt') : null,
        rgb: rgb ? this.numberState(rgb, 'rgb') : null,
        supportsBrightness: Boolean(brightness),
        supportsColorTemperature: Boolean(colorTemperature),
        supportsRgb: Boolean(rgb),
      });
    }
    return features;
  }

  private static coverFeatures(snapshotArg: IBoschShcSnapshot): IBoschShcCoverFeature[] {
    const features: IBoschShcCoverFeature[] = [];
    for (const device of this.activeDevices(snapshotArg)) {
      const shutter = this.service(snapshotArg, device, 'ShutterControl');
      if (!shutter) continue;
      const level = this.numberState(shutter, 'level');
      const position = typeof level === 'number' ? Math.round(level * 100) : null;
      const operationState = this.stringState(shutter, 'operationState');
      const model = device.deviceModel || '';
      features.push({
        ...this.featureBase(device, 'cover', 'cover', this.uniqueId(device, 'cover', true), device.name || 'Bosch SHC cover'),
        serviceId: 'ShutterControl',
        deviceClass: model === 'MICROMODULE_AWNING' ? 'awning' : model === 'MICROMODULE_BLINDS' ? 'blind' : 'shutter',
        state: this.coverState(operationState, position),
        position,
        operationState: operationState ?? undefined,
        calibrated: this.booleanState(shutter, 'calibrated'),
        supportsOpen: true,
        supportsClose: true,
        supportsStop: true,
        supportsPosition: true,
      });
    }
    return features;
  }

  private static climateFeatures(snapshotArg: IBoschShcSnapshot): IBoschShcClimateFeature[] {
    const features: IBoschShcClimateFeature[] = [];
    for (const device of this.activeDevices(snapshotArg)) {
      const roomClimate = this.service(snapshotArg, device, 'RoomClimateControl');
      const heatingCircuit = this.service(snapshotArg, device, 'HeatingCircuit');
      const climateService = roomClimate || heatingCircuit;
      if (!climateService) continue;

      const temperature = this.service(snapshotArg, device, 'TemperatureLevel');
      const summerMode = roomClimate ? this.booleanState(roomClimate, 'summerMode') : undefined;
      features.push({
        ...this.featureBase(device, 'climate', 'climate', this.uniqueId(device, 'climate', true), device.name || 'Bosch SHC climate'),
        serviceId: roomClimate ? 'RoomClimateControl' : 'HeatingCircuit',
        hvacMode: summerMode === true ? 'off' : 'heat',
        currentTemperature: temperature ? this.numberState(temperature, 'temperature') : null,
        targetTemperature: this.numberState(climateService, 'setpointTemperature'),
        operationMode: this.stringState(climateService, 'operationMode') ?? undefined,
        boostMode: roomClimate ? this.booleanState(roomClimate, 'boostMode') : undefined,
        low: roomClimate ? this.booleanState(roomClimate, 'low') : undefined,
        summerMode,
        supportsBoostMode: roomClimate ? this.booleanState(roomClimate, 'supportsBoostMode') : undefined,
        supportedHvacModes: ['heat', 'off'],
      });
    }
    return features;
  }

  private static switchCommand(snapshotArg: IBoschShcSnapshot, requestArg: IServiceCallRequest): IBoschShcModeledCommand | { error: string } | undefined {
    if (!['turn_on', 'turn_off'].includes(requestArg.service)) {
      return { error: `Unsupported Bosch SHC switch service: ${requestArg.service}` };
    }
    const feature = this.resolveFeature(snapshotArg, requestArg, 'switch') as IBoschShcSwitchFeature | undefined;
    if (!feature) {
      return { error: 'Bosch SHC switch service calls require a switch entity target, or an unambiguous switch device target.' };
    }
    const service = this.service(snapshotArg, feature.device, feature.serviceId);
    if (!service) return { error: `Bosch SHC switch service ${feature.serviceId} is missing from snapshot.` };
    const value = requestArg.service === 'turn_on' ? feature.onValue : feature.offValue;
    return this.putServiceState(feature.device, service, { [feature.stateKey]: value }, requestArg);
  }

  private static lightCommand(snapshotArg: IBoschShcSnapshot, requestArg: IServiceCallRequest): IBoschShcModeledCommand | { error: string } | undefined {
    if (!['turn_on', 'turn_off'].includes(requestArg.service)) {
      return { error: `Unsupported Bosch SHC light service: ${requestArg.service}` };
    }
    const feature = this.resolveFeature(snapshotArg, requestArg, 'light') as IBoschShcLightFeature | undefined;
    if (!feature) {
      return { error: 'Bosch SHC light service calls require a light entity target, or an unambiguous light device target.' };
    }

    const switchService = this.service(snapshotArg, feature.device, feature.switchServiceId);
    if (!switchService) return { error: `Bosch SHC light service ${feature.switchServiceId} is missing from snapshot.` };

    const patch: Record<string, unknown> = feature.switchServiceId === 'BinarySwitch'
      ? { on: requestArg.service === 'turn_on' }
      : { switchState: requestArg.service === 'turn_on' ? 'ON' : 'OFF' };
    if (requestArg.service === 'turn_on') {
      const brightness = requestArg.data?.brightness;
      const brightnessPct = requestArg.data?.brightness_pct;
      if (brightness !== undefined && !this.isByte(brightness)) {
        return { error: 'Bosch SHC light brightness must be an integer between 0 and 255.' };
      }
      if (brightnessPct !== undefined && !this.isPercent(brightnessPct)) {
        return { error: 'Bosch SHC light brightness_pct must be an integer between 0 and 100.' };
      }
      if (brightness !== undefined || brightnessPct !== undefined) {
        const brightnessService = this.service(snapshotArg, feature.device, 'MultiLevelSwitch');
        if (!brightnessService) return { error: 'Bosch SHC light brightness is not supported by this device.' };
        const level = typeof brightnessPct === 'number' ? brightnessPct : Math.round((brightness as number) * 100 / 255);
        return this.putServiceState(feature.device, brightnessService, { level }, requestArg);
      }
    }
    return this.putServiceState(feature.device, switchService, patch, requestArg);
  }

  private static coverCommand(snapshotArg: IBoschShcSnapshot, requestArg: IServiceCallRequest): IBoschShcModeledCommand | { error: string } | undefined {
    const supported = ['open_cover', 'close_cover', 'stop_cover', 'set_cover_position'];
    if (!supported.includes(requestArg.service)) {
      return { error: `Unsupported Bosch SHC cover service: ${requestArg.service}` };
    }
    const feature = this.resolveFeature(snapshotArg, requestArg, 'cover') as IBoschShcCoverFeature | undefined;
    if (!feature) {
      return { error: 'Bosch SHC cover service calls require a cover entity target, or an unambiguous cover device target.' };
    }
    const service = this.service(snapshotArg, feature.device, 'ShutterControl');
    if (!service) return { error: 'Bosch SHC ShutterControl service is missing from snapshot.' };

    if (requestArg.service === 'open_cover') return this.putServiceState(feature.device, service, { level: 1.0 }, requestArg);
    if (requestArg.service === 'close_cover') return this.putServiceState(feature.device, service, { level: 0.0 }, requestArg);
    if (requestArg.service === 'stop_cover') return this.putServiceState(feature.device, service, { operationState: 'STOPPED' }, requestArg);
    const position = requestArg.data?.position;
    if (!this.isPercent(position)) {
      return { error: 'Bosch SHC set_cover_position requires data.position as an integer between 0 and 100.' };
    }
    return this.putServiceState(feature.device, service, { level: position / 100 }, requestArg);
  }

  private static climateCommand(snapshotArg: IBoschShcSnapshot, requestArg: IServiceCallRequest): IBoschShcModeledCommand | { error: string } | undefined {
    if (requestArg.service !== 'set_temperature') {
      return { error: `Unsupported Bosch SHC climate service: ${requestArg.service}` };
    }
    const feature = this.resolveFeature(snapshotArg, requestArg, 'climate') as IBoschShcClimateFeature | undefined;
    if (!feature) {
      return { error: 'Bosch SHC climate service calls require a climate entity target, or an unambiguous climate device target.' };
    }
    const temperature = requestArg.data?.temperature;
    if (typeof temperature !== 'number' || !Number.isFinite(temperature)) {
      return { error: 'Bosch SHC set_temperature requires data.temperature as a finite number.' };
    }
    const service = this.service(snapshotArg, feature.device, feature.serviceId);
    if (!service) return { error: `Bosch SHC climate service ${feature.serviceId} is missing from snapshot.` };
    return this.putServiceState(feature.device, service, { setpointTemperature: temperature }, requestArg);
  }

  private static resolveFeature(snapshotArg: IBoschShcSnapshot, requestArg: IServiceCallRequest, platformArg: TBoschShcFeature['platform']): TBoschShcFeature | undefined {
    const features = this.features(snapshotArg).filter((featureArg) => featureArg.platform === platformArg);
    if (requestArg.target.entityId) {
      const entities = this.toEntities(snapshotArg);
      const entity = entities.find((entityArg) => entityArg.id === requestArg.target.entityId);
      return features.find((featureArg) => featureArg.uniqueId === entity?.uniqueId);
    }
    if (requestArg.target.deviceId) {
      const deviceFeatures = features.filter((featureArg) => this.deviceId(featureArg.device) === requestArg.target.deviceId);
      return deviceFeatures.length === 1 ? deviceFeatures[0] : undefined;
    }
    return undefined;
  }

  private static putServiceState(deviceArg: IBoschShcDevice, serviceArg: IBoschShcDeviceService, patchArg: Record<string, unknown>, requestArg: IServiceCallRequest): IBoschShcModeledCommand {
    const stateType = typeof serviceArg.state?.['@type'] === 'string' ? serviceArg.state['@type'] : undefined;
    return {
      action: 'put_device_service_state',
      method: 'PUT',
      path: `/devices/${this.encodeDeviceId(deviceArg.id)}/services/${serviceArg.id}/state`,
      body: stateType ? { '@type': stateType, ...patchArg } : patchArg,
      deviceId: deviceArg.id,
      serviceId: serviceArg.id,
      domain: requestArg.domain,
      service: requestArg.service,
    };
  }

  private static sensorFeature(deviceArg: IBoschShcDevice, keyArg: string, valueArg: string | number | null, serviceArg: IBoschShcDeviceService, attributesArg: Record<string, unknown> = {}): IBoschShcSensorFeature {
    const metadata = sensorMetadata[keyArg] || { name: this.humanize(keyArg) };
    void serviceArg;
    return {
      ...this.featureBase(deviceArg, 'sensor', keyArg, this.uniqueId(deviceArg, keyArg), `${deviceArg.name || 'Bosch SHC device'} ${metadata.name.toLowerCase()}`),
      nativeValue: valueArg,
      unit: metadata.unit,
      deviceClass: metadata.deviceClass,
      stateClass: metadata.stateClass,
      attributes: attributesArg,
    };
  }

  private static featureBase<TPlatform extends TBoschShcFeature['platform']>(deviceArg: IBoschShcDevice, platformArg: TPlatform, aliasArg: string, uniqueIdArg: string, nameArg: string): IBoschShcFeatureBase & { platform: TPlatform } {
    return {
      platform: platformArg,
      id: `${platformArg}_${this.slug(aliasArg)}`,
      alias: aliasArg,
      uniqueId: uniqueIdArg,
      name: nameArg,
      deviceId: deviceArg.id,
      device: deviceArg,
      available: this.available(deviceArg),
    };
  }

  private static service(snapshotArg: IBoschShcSnapshot, deviceArg: IBoschShcDevice, serviceIdArg: string): IBoschShcDeviceService | undefined {
    return deviceArg.services?.find((serviceArg) => serviceArg.id === serviceIdArg)
      || snapshotArg.services?.find((serviceArg) => serviceArg.deviceId === deviceArg.id && serviceArg.id === serviceIdArg);
  }

  private static activeDevices(snapshotArg: IBoschShcSnapshot): IBoschShcDevice[] {
    return snapshotArg.devices.filter((deviceArg) => !deviceArg.deleted);
  }

  private static hasService(deviceArg: IBoschShcDevice, serviceIdArg: string): boolean {
    return Boolean(deviceArg.deviceServiceIds?.includes(serviceIdArg) || deviceArg.services?.some((serviceArg) => serviceArg.id === serviceIdArg));
  }

  private static stringState(serviceArg: IBoschShcDeviceService, keyArg: string): string | null {
    const value = serviceArg.state?.[keyArg];
    return typeof value === 'string' ? value : null;
  }

  private static numberState(serviceArg: IBoschShcDeviceService, keyArg: string): number | null {
    const value = serviceArg.state?.[keyArg];
    if (typeof value === 'number' && Number.isFinite(value)) return value;
    if (typeof value === 'string' && value.trim()) {
      const numberValue = Number(value);
      return Number.isFinite(numberValue) ? numberValue : null;
    }
    return null;
  }

  private static numberNestedState(serviceArg: IBoschShcDeviceService, objectKeyArg: string, keyArg: string): number | null {
    const record = asRecord(serviceArg.state?.[objectKeyArg]);
    const value = record?.[keyArg];
    return typeof value === 'number' && Number.isFinite(value) ? value : null;
  }

  private static booleanState(serviceArg: IBoschShcDeviceService, keyArg: string): boolean | undefined {
    const value = serviceArg.state?.[keyArg];
    return typeof value === 'boolean' ? value : undefined;
  }

  private static batteryWarningLevel(serviceArg: IBoschShcDeviceService): string {
    const entry = serviceArg.faults?.entries?.[0];
    return typeof entry?.type === 'string' ? entry.type : 'OK';
  }

  private static coverState(operationStateArg: string | null, positionArg: number | null): TBoschShcCoverState {
    if (operationStateArg === 'OPENING') return 'opening';
    if (operationStateArg === 'CLOSING') return 'closing';
    if (positionArg === 0) return 'closed';
    if (typeof positionArg === 'number' && positionArg > 0) return 'open';
    if (operationStateArg === 'STOPPED') return 'stopped';
    return 'unknown';
  }

  private static available(deviceArg: IBoschShcDevice): boolean {
    return deviceArg.status === undefined || deviceArg.status === 'AVAILABLE';
  }

  private static manufacturer(deviceArg: IBoschShcDevice): string {
    const value = deviceArg.manufacturer || 'Bosch';
    return value.toUpperCase() === 'BOSCH' ? 'Bosch' : value;
  }

  private static uniqueId(deviceArg: IBoschShcDevice, aliasArg: string, defaultArg = false): string {
    const base = deviceArg.serial || deviceArg.id;
    return defaultArg ? base : `${base}_${aliasArg}`;
  }

  private static usesDefaultEntityObjectId(featureArg: TBoschShcFeature): boolean {
    return featureArg.platform === 'binary_sensor' && featureArg.alias === 'contact'
      || featureArg.platform === 'switch' && featureArg.alias === 'power'
      || featureArg.platform === 'light' && featureArg.alias === 'light'
      || featureArg.platform === 'cover' && featureArg.alias === 'cover'
      || featureArg.platform === 'climate' && featureArg.alias === 'climate';
  }

  private static controllerUniqueId(snapshotArg: IBoschShcSnapshot): string | undefined {
    return formatMac(snapshotArg.uniqueId || snapshotArg.information?.macAddress || '') || snapshotArg.host;
  }

  private static encodeDeviceId(valueArg: string): string {
    return valueArg.replace(/#/g, '%23');
  }

  private static isByte(valueArg: unknown): valueArg is number {
    return typeof valueArg === 'number' && Number.isInteger(valueArg) && valueArg >= 0 && valueArg <= 255;
  }

  private static isPercent(valueArg: unknown): valueArg is number {
    return typeof valueArg === 'number' && Number.isInteger(valueArg) && valueArg >= 0 && valueArg <= 100;
  }

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

  private static humanize(valueArg: string): string {
    return valueArg.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
  }
}

export const formatMac = (valueArg?: string): string | undefined => {
  if (!valueArg) return undefined;
  const trimmed = valueArg.trim();
  if (/^[0-9a-f]{2}(-[0-9a-f]{2}){5}$/i.test(trimmed)) return trimmed.toLowerCase();
  const compact = trimmed.replace(/[:.-]/g, '').toLowerCase();
  if (!/^[0-9a-f]{12}$/.test(compact)) return undefined;
  return compact.match(/../g)?.join('-');
};

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