import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IIntegrationEvent } from '../../core/types.js';
import type {
  IOpenthermGwBinarySensorDescription,
  IOpenthermGwClimateState,
  IOpenthermGwEvent,
  IOpenthermGwSensorDescription,
  IOpenthermGwSnapshot,
  IOpenthermGwStatus,
  IOpenthermGwSwitchDescription,
  IOpenthermGwWaterHeaterState,
  TOpenthermGwDataSource,
  TOpenthermGwStatusValue,
} from './opentherm_gw.types.js';
import { openthermGwDomain } from './opentherm_gw.types.js';

export const openthermGwSensorDescriptions: IOpenthermGwSensorDescription[] = [
  { source: 'boiler', key: 'control_setpoint', name: 'Control setpoint 1', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'control_setpoint_2', name: 'Control setpoint 2', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'relative_mod_level', name: 'Relative modulation level', unit: '%', stateClass: 'measurement' },
  { source: 'boiler', key: 'ch_water_pressure', name: 'Central heating water pressure', unit: 'bar', deviceClass: 'pressure', stateClass: 'measurement' },
  { source: 'boiler', key: 'dhw_flow_rate', name: 'Hot water flow rate', unit: 'L/min', stateClass: 'measurement' },
  { source: 'boiler', key: 'ch_water_temp', name: 'Central heating 1 water temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'ch_water_temp_2', name: 'Central heating 2 water temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'dhw_temp', name: 'Hot water 1 temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'dhw_temp_2', name: 'Hot water 2 temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'outside_temp', name: 'Outside temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'return_water_temp', name: 'Return water temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'exhaust_temp', name: 'Exhaust temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'dhw_setpoint', name: 'Hot water setpoint', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'max_ch_setpoint', name: 'Max central heating setpoint', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'boiler', key: 'slave_max_relative_modulation', name: 'Max relative modulation level', unit: '%', stateClass: 'measurement' },
  { source: 'boiler', key: 'slave_max_capacity', name: 'Max capacity', unit: 'kW', stateClass: 'measurement' },
  { source: 'boiler', key: 'slave_min_mod_level', name: 'Min modulation level', unit: '%', stateClass: 'measurement' },
  { source: 'boiler', key: 'burner_starts', name: 'Total burner starts', unit: 'starts', stateClass: 'total' },
  { source: 'boiler', key: 'ch_pump_starts', name: 'Central heating pump starts', unit: 'starts', stateClass: 'total' },
  { source: 'boiler', key: 'dhw_pump_starts', name: 'Hot water pump starts', unit: 'starts', stateClass: 'total' },
  { source: 'boiler', key: 'dhw_burner_starts', name: 'Hot water burner starts', unit: 'starts', stateClass: 'total' },
  { source: 'boiler', key: 'burner_hours', name: 'Total burner hours', unit: 'h', stateClass: 'total' },
  { source: 'boiler', key: 'ch_pump_hours', name: 'Central heating pump hours', unit: 'h', stateClass: 'total' },
  { source: 'boiler', key: 'dhw_pump_hours', name: 'Hot water pump hours', unit: 'h', stateClass: 'total' },
  { source: 'boiler', key: 'dhw_burner_hours', name: 'Hot water burner hours', unit: 'h', stateClass: 'total' },
  { source: 'boiler', key: 'slave_memberid', name: 'Manufacturer ID' },
  { source: 'boiler', key: 'slave_oem_fault', name: 'OEM fault code' },
  { source: 'boiler', key: 'oem_diag', name: 'OEM diagnostic code' },
  { source: 'boiler', key: 'slave_ot_version', name: 'OpenTherm version' },
  { source: 'boiler', key: 'slave_product_type', name: 'Product type' },
  { source: 'boiler', key: 'slave_product_version', name: 'Product version' },
  { source: 'thermostat', key: 'room_temp', name: 'Room temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'room_setpoint', name: 'Room setpoint 1', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'room_setpoint_2', name: 'Room setpoint 2', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'room_setpoint_ovrd', name: 'Room setpoint override', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'outside_temp', name: 'Outside temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'control_setpoint', name: 'Control setpoint 1', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'control_setpoint_2', name: 'Control setpoint 2', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'thermostat', key: 'master_memberid', name: 'Manufacturer ID' },
  { source: 'thermostat', key: 'master_ot_version', name: 'OpenTherm version' },
  { source: 'thermostat', key: 'master_product_type', name: 'Product type' },
  { source: 'thermostat', key: 'master_product_version', name: 'Product version' },
  { source: 'gateway', key: 'otgw_mode', name: 'Operating mode' },
  { source: 'gateway', key: 'otgw_dhw_ovrd', name: 'Hot water override mode' },
  { source: 'gateway', key: 'otgw_about', name: 'Firmware version' },
  { source: 'gateway', key: 'otgw_build', name: 'Firmware build' },
  { source: 'gateway', key: 'otgw_clockmhz', name: 'Clock speed' },
  { source: 'gateway', key: 'otgw_led_a', name: 'LED A mode' },
  { source: 'gateway', key: 'otgw_led_b', name: 'LED B mode' },
  { source: 'gateway', key: 'otgw_led_c', name: 'LED C mode' },
  { source: 'gateway', key: 'otgw_led_d', name: 'LED D mode' },
  { source: 'gateway', key: 'otgw_led_e', name: 'LED E mode' },
  { source: 'gateway', key: 'otgw_led_f', name: 'LED F mode' },
  { source: 'gateway', key: 'otgw_gpio_a', name: 'GPIO A mode' },
  { source: 'gateway', key: 'otgw_gpio_b', name: 'GPIO B mode' },
  { source: 'gateway', key: 'otgw_reset_cause', name: 'Reset cause' },
  { source: 'gateway', key: 'otgw_setback_temp', name: 'Setback temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement' },
  { source: 'gateway', key: 'otgw_setpoint_ovrd_mode', name: 'Room setpoint override mode' },
  { source: 'gateway', key: 'otgw_smart_pwr', name: 'Smart power mode' },
  { source: 'gateway', key: 'otgw_temp_sensor', name: 'Temperature sensor function' },
  { source: 'gateway', key: 'otgw_thermostat_detect', name: 'Thermostat detection mode' },
  { source: 'gateway', key: 'otgw_vref', name: 'Reference voltage' },
];

export const openthermGwBinarySensorDescriptions: IOpenthermGwBinarySensorDescription[] = [
  { source: 'boiler', key: 'slave_fault_indication', name: 'Fault indication', deviceClass: 'problem' },
  { source: 'boiler', key: 'slave_ch_active', name: 'Central heating 1', deviceClass: 'running' },
  { source: 'boiler', key: 'slave_ch2_active', name: 'Central heating 2', deviceClass: 'running' },
  { source: 'boiler', key: 'slave_dhw_active', name: 'Hot water', deviceClass: 'running' },
  { source: 'boiler', key: 'slave_flame_on', name: 'Flame', deviceClass: 'heat' },
  { source: 'boiler', key: 'slave_cooling_active', name: 'Cooling', deviceClass: 'running' },
  { source: 'boiler', key: 'slave_diagnostic_indication', name: 'Diagnostic indication', deviceClass: 'problem' },
  { source: 'boiler', key: 'slave_dhw_present', name: 'Supports hot water' },
  { source: 'boiler', key: 'slave_control_type', name: 'Control type' },
  { source: 'boiler', key: 'slave_cooling_supported', name: 'Supports cooling' },
  { source: 'boiler', key: 'slave_dhw_config', name: 'Hot water config' },
  { source: 'boiler', key: 'slave_master_low_off_pump', name: 'Supports pump control' },
  { source: 'boiler', key: 'slave_ch2_present', name: 'Supports central heating 2' },
  { source: 'boiler', key: 'slave_service_required', name: 'Service required', deviceClass: 'problem' },
  { source: 'boiler', key: 'slave_remote_reset', name: 'Supports remote reset' },
  { source: 'boiler', key: 'slave_low_water_pressure', name: 'Low water pressure', deviceClass: 'problem' },
  { source: 'boiler', key: 'slave_gas_fault', name: 'Gas fault', deviceClass: 'problem' },
  { source: 'boiler', key: 'slave_air_pressure_fault', name: 'Air pressure fault', deviceClass: 'problem' },
  { source: 'boiler', key: 'slave_water_overtemp', name: 'Water overtemperature', deviceClass: 'problem' },
  { source: 'boiler', key: 'remote_transfer_max_ch', name: 'Central heating setpoint transfer support' },
  { source: 'boiler', key: 'remote_rw_max_ch', name: 'Central heating setpoint write support' },
  { source: 'boiler', key: 'remote_transfer_dhw', name: 'Hot water setpoint transfer support' },
  { source: 'boiler', key: 'remote_rw_dhw', name: 'Hot water setpoint write support' },
  { source: 'thermostat', key: 'master_ch_enabled', name: 'Central heating 1' },
  { source: 'thermostat', key: 'master_ch2_enabled', name: 'Central heating 2' },
  { source: 'thermostat', key: 'master_dhw_enabled', name: 'Hot water' },
  { source: 'thermostat', key: 'master_cooling_enabled', name: 'Cooling' },
  { source: 'thermostat', key: 'master_otc_enabled', name: 'Outside temperature correction' },
  { source: 'gateway', key: 'otgw_gpio_a_state', name: 'GPIO A state' },
  { source: 'gateway', key: 'otgw_gpio_b_state', name: 'GPIO B state' },
  { source: 'gateway', key: 'otgw_ignore_transitions', name: 'Ignore transitions' },
  { source: 'gateway', key: 'otgw_ovrd_high_byte', name: 'Override high byte' },
];

export const openthermGwSwitchDescriptions: IOpenthermGwSwitchDescription[] = [
  { key: 'central_heating_1_override', name: 'Central heating 1 override', circuit: 1 },
  { key: 'central_heating_2_override', name: 'Central heating 2 override', circuit: 2 },
];

export class OpenthermGwMapper {
  public static toDevices(snapshotArg: IOpenthermGwSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    return [
      this.gatewayDevice(snapshotArg, updatedAt),
      this.boilerDevice(snapshotArg, updatedAt),
      this.thermostatDevice(snapshotArg, updatedAt),
    ];
  }

  public static toEntities(snapshotArg: IOpenthermGwSnapshot): IIntegrationEntity[] {
    return [
      this.climateEntity(snapshotArg),
      ...this.sensorEntities(snapshotArg),
      ...this.binarySensorEntities(snapshotArg),
      ...this.switchEntities(snapshotArg),
    ];
  }

  public static toClimate(snapshotArg: IOpenthermGwSnapshot): IOpenthermGwClimateState {
    const boiler = snapshotArg.status.boiler;
    const thermostat = snapshotArg.status.thermostat;
    const heating = boolValue(boiler.slave_ch_active) && boolValue(boiler.slave_flame_on);
    const cooling = boolValue(boiler.slave_cooling_active);
    const hvacAction = cooling ? 'cooling' : heating ? 'heating' : snapshotArg.online ? 'idle' : 'off';
    return {
      currentTemperature: numberValue(thermostat.room_temp),
      targetTemperature: numberValue(thermostat.room_setpoint_ovrd) ?? numberValue(thermostat.room_setpoint),
      hvacMode: cooling ? 'cool' : snapshotArg.online ? 'heat' : 'off',
      hvacAction,
      presetMode: this.awayMode(snapshotArg) ? 'away' : 'none',
      minTemp: 1,
      maxTemp: 30,
      temperatureUnit: 'C',
    };
  }

  public static toWaterHeater(snapshotArg: IOpenthermGwSnapshot): IOpenthermGwWaterHeaterState {
    const boiler = snapshotArg.status.boiler;
    const gateway = snapshotArg.status.gateway;
    const active = boolValue(boiler.slave_dhw_active);
    const dhwOverride = gateway.otgw_dhw_ovrd;
    return {
      currentTemperature: numberValue(boiler.dhw_temp),
      targetTemperature: numberValue(boiler.dhw_setpoint),
      operationMode: active ? 'heat' : dhwOverride === 0 || dhwOverride === '0' ? 'off' : snapshotArg.online ? 'auto' : 'unknown',
      hotWaterActive: active,
      temperatureUnit: 'C',
    };
  }

  public static toIntegrationEvent(eventArg: IOpenthermGwEvent): IIntegrationEvent {
    return {
      type: eventArg.type === 'error' ? 'error' : 'state_changed',
      integrationDomain: openthermGwDomain,
      deviceId: `opentherm_gw.gateway.${this.slug(eventArg.gatewayId)}`,
      data: eventArg,
      timestamp: eventArg.timestamp,
    };
  }

  public static gatewayDeviceId(snapshotArg: IOpenthermGwSnapshot): string {
    return `opentherm_gw.gateway.${this.uniqueBase(snapshotArg)}`;
  }

  public static boilerDeviceId(snapshotArg: IOpenthermGwSnapshot): string {
    return `opentherm_gw.boiler.${this.uniqueBase(snapshotArg)}`;
  }

  public static thermostatDeviceId(snapshotArg: IOpenthermGwSnapshot): string {
    return `opentherm_gw.thermostat.${this.uniqueBase(snapshotArg)}`;
  }

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

  private static gatewayDevice(snapshotArg: IOpenthermGwSnapshot, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    const gateway = snapshotArg.status.gateway;
    return {
      id: this.gatewayDeviceId(snapshotArg),
      integrationDomain: openthermGwDomain,
      name: snapshotArg.gateway.name || 'OpenTherm Gateway',
      protocol: 'unknown',
      manufacturer: 'Schelte Bron',
      model: 'OpenTherm Gateway',
      online: snapshotArg.online,
      features: [
        { id: 'connection', capability: 'sensor', name: 'Connection', readable: true, writable: false },
        { id: 'mode', capability: 'sensor', name: 'Operating mode', readable: true, writable: true },
        { id: 'hot_water_override', capability: 'switch', name: 'Hot water override', readable: true, writable: true },
        { id: 'outside_temperature', capability: 'sensor', name: 'Outside temperature override', readable: true, writable: true, unit: 'C' },
        { id: 'reset', capability: 'switch', name: 'Reset', readable: false, writable: true },
      ],
      state: [
        { featureId: 'connection', value: snapshotArg.online ? 'online' : 'offline', updatedAt: updatedAtArg },
        { featureId: 'mode', value: primitiveValue(gateway.otgw_mode) ?? null, updatedAt: updatedAtArg },
        { featureId: 'hot_water_override', value: primitiveValue(gateway.otgw_dhw_ovrd) ?? null, updatedAt: updatedAtArg },
      ],
      metadata: {
        host: snapshotArg.gateway.host,
        port: snapshotArg.gateway.port,
        device: snapshotArg.gateway.device,
        firmwareVersion: snapshotArg.gateway.firmwareVersion,
        source: snapshotArg.source,
      },
    };
  }

  private static boilerDevice(snapshotArg: IOpenthermGwSnapshot, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    const boiler = snapshotArg.status.boiler;
    return {
      id: this.boilerDeviceId(snapshotArg),
      integrationDomain: openthermGwDomain,
      name: `${snapshotArg.gateway.name || 'OpenTherm'} Boiler`,
      protocol: 'unknown',
      manufacturer: stringValue(boiler.slave_memberid),
      model: stringValue(boiler.slave_product_type) || 'OpenTherm Boiler',
      online: snapshotArg.online,
      features: [
        { id: 'flame', capability: 'sensor', name: 'Flame', readable: true, writable: false },
        { id: 'central_heating', capability: 'climate', name: 'Central heating', readable: true, writable: true },
        { id: 'hot_water', capability: 'climate', name: 'Hot water', readable: true, writable: true },
        { id: 'control_setpoint', capability: 'climate', name: 'Control setpoint', readable: true, writable: true, unit: 'C' },
        { id: 'dhw_setpoint', capability: 'climate', name: 'Hot water setpoint', readable: true, writable: true, unit: 'C' },
        { id: 'water_temperature', capability: 'sensor', name: 'Water temperature', readable: true, writable: false, unit: 'C' },
      ],
      state: [
        { featureId: 'flame', value: boolValue(boiler.slave_flame_on), updatedAt: updatedAtArg },
        { featureId: 'central_heating', value: boolValue(boiler.slave_ch_active), updatedAt: updatedAtArg },
        { featureId: 'hot_water', value: boolValue(boiler.slave_dhw_active), updatedAt: updatedAtArg },
        { featureId: 'control_setpoint', value: numberValue(boiler.control_setpoint) ?? null, updatedAt: updatedAtArg },
        { featureId: 'dhw_setpoint', value: numberValue(boiler.dhw_setpoint) ?? null, updatedAt: updatedAtArg },
        { featureId: 'water_temperature', value: numberValue(boiler.ch_water_temp) ?? null, updatedAt: updatedAtArg },
      ],
      metadata: {
        productVersion: boiler.slave_product_version,
        openthermVersion: boiler.slave_ot_version,
      },
    };
  }

  private static thermostatDevice(snapshotArg: IOpenthermGwSnapshot, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    const thermostat = snapshotArg.status.thermostat;
    const climate = this.toClimate(snapshotArg);
    return {
      id: this.thermostatDeviceId(snapshotArg),
      integrationDomain: openthermGwDomain,
      name: `${snapshotArg.gateway.name || 'OpenTherm'} Thermostat`,
      protocol: 'unknown',
      manufacturer: stringValue(thermostat.master_memberid),
      model: stringValue(thermostat.master_product_type) || 'OpenTherm Thermostat',
      online: snapshotArg.online,
      features: [
        { id: 'current_temperature', capability: 'climate', name: 'Current temperature', readable: true, writable: false, unit: 'C' },
        { id: 'target_temperature', capability: 'climate', name: 'Target temperature', readable: true, writable: true, unit: 'C' },
        { id: 'hvac_action', capability: 'climate', name: 'HVAC action', readable: true, writable: false },
      ],
      state: [
        { featureId: 'current_temperature', value: climate.currentTemperature ?? null, updatedAt: updatedAtArg },
        { featureId: 'target_temperature', value: climate.targetTemperature ?? null, updatedAt: updatedAtArg },
        { featureId: 'hvac_action', value: climate.hvacAction, updatedAt: updatedAtArg },
      ],
      metadata: {
        productVersion: thermostat.master_product_version,
        openthermVersion: thermostat.master_ot_version,
      },
    };
  }

  private static climateEntity(snapshotArg: IOpenthermGwSnapshot): IIntegrationEntity {
    const climate = snapshotArg.climate || this.toClimate(snapshotArg);
    const name = `${snapshotArg.gateway.name || 'OpenTherm Gateway'} Thermostat`;
    return {
      id: `climate.${this.slug(name)}`,
      uniqueId: `opentherm_gw_${this.uniqueBase(snapshotArg)}_thermostat_climate`,
      integrationDomain: openthermGwDomain,
      deviceId: this.thermostatDeviceId(snapshotArg),
      platform: 'climate',
      name,
      state: climate.hvacMode,
      attributes: {
        currentTemperature: climate.currentTemperature,
        targetTemperature: climate.targetTemperature,
        hvacAction: climate.hvacAction,
        presetMode: climate.presetMode,
        minTemp: climate.minTemp,
        maxTemp: climate.maxTemp,
        temperatureUnit: climate.temperatureUnit,
        supportedFeatures: ['target_temperature', 'preset_mode'],
      },
      available: snapshotArg.online,
    };
  }

  private static sensorEntities(snapshotArg: IOpenthermGwSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    const described = new Set<string>();
    for (const description of openthermGwSensorDescriptions) {
      described.add(descriptionKey(description.source, description.key));
      const value = statusPart(snapshotArg.status, description.source)[description.key];
      if (value === undefined) {
        continue;
      }
      entities.push(this.sensorEntity(snapshotArg, description, value));
    }

    const binaryKeys = new Set(openthermGwBinarySensorDescriptions.map((descriptionArg) => descriptionKey(descriptionArg.source, descriptionArg.key)));
    for (const source of ['gateway', 'boiler', 'thermostat'] as TOpenthermGwDataSource[]) {
      for (const [key, value] of Object.entries(statusPart(snapshotArg.status, source))) {
        const combinedKey = descriptionKey(source, key);
        if (described.has(combinedKey) || binaryKeys.has(combinedKey) || value === undefined || typeof value === 'object') {
          continue;
        }
        entities.push(this.sensorEntity(snapshotArg, { source, key, name: titleCase(key) }, value));
      }
    }
    return entities;
  }

  private static sensorEntity(snapshotArg: IOpenthermGwSnapshot, descriptionArg: IOpenthermGwSensorDescription, valueArg: TOpenthermGwStatusValue): IIntegrationEntity {
    const sourceName = sourceLabel(descriptionArg.source);
    const entityName = `${snapshotArg.gateway.name || 'OpenTherm Gateway'} ${sourceName} ${descriptionArg.name}`;
    return {
      id: `sensor.${this.slug(entityName)}`,
      uniqueId: `opentherm_gw_${this.uniqueBase(snapshotArg)}_${descriptionArg.source}_${this.slug(descriptionArg.key)}`,
      integrationDomain: openthermGwDomain,
      deviceId: this.deviceIdForSource(snapshotArg, descriptionArg.source),
      platform: 'sensor',
      name: entityName,
      state: primitiveValue(valueArg) ?? 'unknown',
      attributes: {
        source: descriptionArg.source,
        key: descriptionArg.key,
        unitOfMeasurement: descriptionArg.unit,
        deviceClass: descriptionArg.deviceClass,
        stateClass: descriptionArg.stateClass,
      },
      available: snapshotArg.online,
    };
  }

  private static binarySensorEntities(snapshotArg: IOpenthermGwSnapshot): IIntegrationEntity[] {
    return openthermGwBinarySensorDescriptions.flatMap((descriptionArg) => {
      const value = statusPart(snapshotArg.status, descriptionArg.source)[descriptionArg.key];
      if (value === undefined) {
        return [];
      }
      const sourceName = sourceLabel(descriptionArg.source);
      const entityName = `${snapshotArg.gateway.name || 'OpenTherm Gateway'} ${sourceName} ${descriptionArg.name}`;
      return [{
        id: `binary_sensor.${this.slug(entityName)}`,
        uniqueId: `opentherm_gw_${this.uniqueBase(snapshotArg)}_${descriptionArg.source}_${this.slug(descriptionArg.key)}`,
        integrationDomain: openthermGwDomain,
        deviceId: this.deviceIdForSource(snapshotArg, descriptionArg.source),
        platform: 'binary_sensor' as const,
        name: entityName,
        state: boolValue(value) ? 'on' : 'off',
        attributes: {
          source: descriptionArg.source,
          key: descriptionArg.key,
          deviceClass: descriptionArg.deviceClass,
        },
        available: snapshotArg.online,
      }];
    });
  }

  private static switchEntities(snapshotArg: IOpenthermGwSnapshot): IIntegrationEntity[] {
    return openthermGwSwitchDescriptions.map((descriptionArg) => {
      const value = this.switchState(snapshotArg, descriptionArg);
      const entityName = `${snapshotArg.gateway.name || 'OpenTherm Gateway'} ${descriptionArg.name}`;
      return {
        id: `switch.${this.slug(entityName)}`,
        uniqueId: `opentherm_gw_${this.uniqueBase(snapshotArg)}_${descriptionArg.key}`,
        integrationDomain: openthermGwDomain,
        deviceId: this.gatewayDeviceId(snapshotArg),
        platform: 'switch' as const,
        name: entityName,
        state: value === undefined ? 'unknown' : value ? 'on' : 'off',
        attributes: {
          key: descriptionArg.key,
          circuit: descriptionArg.circuit,
          assumedState: true,
        },
        available: snapshotArg.online,
      };
    });
  }

  private static switchState(snapshotArg: IOpenthermGwSnapshot, descriptionArg: IOpenthermGwSwitchDescription): boolean | undefined {
    const gatewayValue = snapshotArg.status.gateway[descriptionArg.key];
    if (gatewayValue !== undefined) {
      return boolValue(gatewayValue);
    }
    const key = descriptionArg.circuit === 2 ? 'master_ch2_enabled' : 'master_ch_enabled';
    const boilerValue = snapshotArg.status.boiler[key];
    if (boilerValue !== undefined) {
      return boolValue(boilerValue);
    }
    const thermostatValue = snapshotArg.status.thermostat[key];
    return thermostatValue === undefined ? undefined : boolValue(thermostatValue);
  }

  private static awayMode(snapshotArg: IOpenthermGwSnapshot): boolean {
    const gateway = snapshotArg.status.gateway;
    const gpioA = numberValue(gateway.otgw_gpio_a);
    const gpioB = numberValue(gateway.otgw_gpio_b);
    const gpioAState = numberValue(gateway.otgw_gpio_a_state);
    const gpioBState = numberValue(gateway.otgw_gpio_b_state);
    return (gpioA === 5 && gpioAState === 0) || (gpioA === 6 && gpioAState === 1) || (gpioB === 5 && gpioBState === 0) || (gpioB === 6 && gpioBState === 1);
  }

  private static deviceIdForSource(snapshotArg: IOpenthermGwSnapshot, sourceArg: TOpenthermGwDataSource): string {
    if (sourceArg === 'boiler') {
      return this.boilerDeviceId(snapshotArg);
    }
    if (sourceArg === 'thermostat') {
      return this.thermostatDeviceId(snapshotArg);
    }
    return this.gatewayDeviceId(snapshotArg);
  }

  private static uniqueBase(snapshotArg: IOpenthermGwSnapshot): string {
    return this.slug(snapshotArg.gateway.id || snapshotArg.gateway.host || snapshotArg.gateway.device || snapshotArg.gateway.name || 'opentherm_gateway');
  }
}

const statusPart = (statusArg: IOpenthermGwStatus, sourceArg: TOpenthermGwDataSource): Record<string, TOpenthermGwStatusValue> => statusArg[sourceArg];

const descriptionKey = (sourceArg: TOpenthermGwDataSource, keyArg: string): string => `${sourceArg}.${keyArg}`;

const sourceLabel = (sourceArg: TOpenthermGwDataSource): string => sourceArg === 'boiler' ? 'Boiler' : sourceArg === 'thermostat' ? 'Thermostat' : 'Gateway';

const boolValue = (valueArg: unknown): boolean => valueArg === true || valueArg === 1 || valueArg === '1' || valueArg === 'true' || valueArg === 'on';

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

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

const primitiveValue = (valueArg: TOpenthermGwStatusValue): string | number | boolean | null | undefined => {
  if (valueArg === undefined) {
    return undefined;
  }
  return valueArg === null ? null : valueArg;
};

const titleCase = (valueArg: string): string => valueArg.replace(/_/g, ' ').replace(/\b\w/g, (matchArg) => matchArg.toUpperCase());
