import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type {
  IHomeWizardBatteriesSnapshot,
  IHomeWizardCommandRequest,
  IHomeWizardConfig,
  IHomeWizardDeviceSnapshot,
  IHomeWizardExternalDevice,
  IHomeWizardMeasurementSnapshot,
  IHomeWizardRawData,
  IHomeWizardSnapshot,
  IHomeWizardStateSnapshot,
  IHomeWizardSystemSnapshot,
  THomeWizardApiVersion,
  THomeWizardProtocol,
  THomeWizardSnapshotSource,
} from './homewizard.types.js';
import {
  homeWizardDefaultHttpPort,
  homeWizardDefaultHttpsPort,
  homeWizardDisplayName,
  homeWizardDomain,
  homeWizardProductNames,
  homeWizardProductTypes,
  homeWizardVerificationIds,
} from './homewizard.types.js';

interface IHomeWizardSnapshotOptions {
  config: IHomeWizardConfig;
  rawData?: Partial<IHomeWizardRawData>;
  online?: boolean;
  source?: THomeWizardSnapshotSource;
  error?: string;
}

interface IHomeWizardSensorDefinition {
  key: string;
  name: string;
  unit?: string;
  deviceClass?: string;
  stateClass?: string;
  entityCategory?: string;
  value(snapshotArg: IHomeWizardSnapshot): unknown;
}

interface IHomeWizardEntityOptions {
  platform: TEntityPlatform;
  key: string;
  name: string;
  state: unknown;
  available: boolean;
  attributes?: Record<string, unknown>;
  deviceId?: string;
}

const batteryManagedProductTypes = new Set<string>([
  homeWizardProductTypes.p1Meter,
  homeWizardProductTypes.energyMeter1Phase,
  homeWizardProductTypes.energyMeter3Phase,
  homeWizardProductTypes.eastronSdm230,
  homeWizardProductTypes.eastronSdm630,
]);

const identifyUnsupportedProductTypes = new Set<string>([
  homeWizardProductTypes.energyMeter1Phase,
  homeWizardProductTypes.energyMeter3Phase,
  homeWizardProductTypes.eastronSdm230,
  homeWizardProductTypes.eastronSdm630,
]);

const productionPowerProductTypes = new Set<string>([
  homeWizardProductTypes.energySocket,
  homeWizardProductTypes.energyMeter1Phase,
  homeWizardProductTypes.energyMeter3Phase,
  homeWizardProductTypes.eastronSdm230,
  homeWizardProductTypes.eastronSdm630,
  homeWizardProductTypes.battery,
]);

const sensorDefinitions: IHomeWizardSensorDefinition[] = [
  sensor('smr_version', 'DSMR version', (snapshotArg) => snapshotArg.measurement.protocol_version, { entityCategory: 'diagnostic' }),
  sensor('meter_model', 'Smart meter model', (snapshotArg) => snapshotArg.measurement.meter_model, { entityCategory: 'diagnostic' }),
  sensor('unique_meter_id', 'Smart meter identifier', (snapshotArg) => snapshotArg.measurement.unique_id, { entityCategory: 'diagnostic' }),
  sensor('wifi_ssid', 'Wi-Fi SSID', (snapshotArg) => snapshotArg.system?.wifi_ssid, { entityCategory: 'diagnostic' }),
  sensor('active_tariff', 'Tariff', (snapshotArg) => valueToString(snapshotArg.measurement.tariff), { deviceClass: 'enum' }),
  sensor('wifi_strength', 'Wi-Fi strength', (snapshotArg) => snapshotArg.system?.wifi_strength_pct, { unit: '%', stateClass: 'measurement', entityCategory: 'diagnostic' }),
  sensor('wifi_rssi', 'Wi-Fi RSSI', (snapshotArg) => snapshotArg.system?.wifi_rssi_db, { unit: 'dB', stateClass: 'measurement', entityCategory: 'diagnostic' }),
  sensor('total_power_import_kwh', 'Energy import', (snapshotArg) => snapshotArg.measurement.energy_import_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_import_t1_kwh', 'Energy import tariff 1', (snapshotArg) => snapshotArg.measurement.energy_import_t1_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_import_t2_kwh', 'Energy import tariff 2', (snapshotArg) => snapshotArg.measurement.energy_import_t2_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_import_t3_kwh', 'Energy import tariff 3', (snapshotArg) => snapshotArg.measurement.energy_import_t3_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_import_t4_kwh', 'Energy import tariff 4', (snapshotArg) => snapshotArg.measurement.energy_import_t4_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_export_kwh', 'Energy export', (snapshotArg) => snapshotArg.measurement.energy_export_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_export_t1_kwh', 'Energy export tariff 1', (snapshotArg) => snapshotArg.measurement.energy_export_t1_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_export_t2_kwh', 'Energy export tariff 2', (snapshotArg) => snapshotArg.measurement.energy_export_t2_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_export_t3_kwh', 'Energy export tariff 3', (snapshotArg) => snapshotArg.measurement.energy_export_t3_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('total_power_export_t4_kwh', 'Energy export tariff 4', (snapshotArg) => snapshotArg.measurement.energy_export_t4_kwh, { unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing' }),
  sensor('active_power_w', 'Power', (snapshotArg) => snapshotArg.measurement.power_w, { unit: 'W', deviceClass: 'power', stateClass: 'measurement' }),
  sensor('active_power_l1_w', 'Power phase 1', (snapshotArg) => snapshotArg.measurement.power_l1_w, { unit: 'W', deviceClass: 'power', stateClass: 'measurement' }),
  sensor('active_power_l2_w', 'Power phase 2', (snapshotArg) => snapshotArg.measurement.power_l2_w, { unit: 'W', deviceClass: 'power', stateClass: 'measurement' }),
  sensor('active_power_l3_w', 'Power phase 3', (snapshotArg) => snapshotArg.measurement.power_l3_w, { unit: 'W', deviceClass: 'power', stateClass: 'measurement' }),
  sensor('active_voltage_v', 'Voltage', (snapshotArg) => snapshotArg.measurement.voltage_v, { unit: 'V', deviceClass: 'voltage', stateClass: 'measurement' }),
  sensor('active_voltage_l1_v', 'Voltage phase 1', (snapshotArg) => snapshotArg.measurement.voltage_l1_v, { unit: 'V', deviceClass: 'voltage', stateClass: 'measurement' }),
  sensor('active_voltage_l2_v', 'Voltage phase 2', (snapshotArg) => snapshotArg.measurement.voltage_l2_v, { unit: 'V', deviceClass: 'voltage', stateClass: 'measurement' }),
  sensor('active_voltage_l3_v', 'Voltage phase 3', (snapshotArg) => snapshotArg.measurement.voltage_l3_v, { unit: 'V', deviceClass: 'voltage', stateClass: 'measurement' }),
  sensor('active_current_a', 'Current', (snapshotArg) => snapshotArg.measurement.current_a, { unit: 'A', deviceClass: 'current', stateClass: 'measurement' }),
  sensor('active_current_l1_a', 'Current phase 1', (snapshotArg) => snapshotArg.measurement.current_l1_a, { unit: 'A', deviceClass: 'current', stateClass: 'measurement' }),
  sensor('active_current_l2_a', 'Current phase 2', (snapshotArg) => snapshotArg.measurement.current_l2_a, { unit: 'A', deviceClass: 'current', stateClass: 'measurement' }),
  sensor('active_current_l3_a', 'Current phase 3', (snapshotArg) => snapshotArg.measurement.current_l3_a, { unit: 'A', deviceClass: 'current', stateClass: 'measurement' }),
  sensor('active_frequency_hz', 'Frequency', (snapshotArg) => snapshotArg.measurement.frequency_hz, { unit: 'Hz', deviceClass: 'frequency', stateClass: 'measurement' }),
  sensor('active_apparent_power_va', 'Apparent power', (snapshotArg) => snapshotArg.measurement.apparent_power_va, { unit: 'VA', deviceClass: 'apparent_power', stateClass: 'measurement' }),
  sensor('active_apparent_power_l1_va', 'Apparent power phase 1', (snapshotArg) => snapshotArg.measurement.apparent_power_l1_va, { unit: 'VA', deviceClass: 'apparent_power', stateClass: 'measurement' }),
  sensor('active_apparent_power_l2_va', 'Apparent power phase 2', (snapshotArg) => snapshotArg.measurement.apparent_power_l2_va, { unit: 'VA', deviceClass: 'apparent_power', stateClass: 'measurement' }),
  sensor('active_apparent_power_l3_va', 'Apparent power phase 3', (snapshotArg) => snapshotArg.measurement.apparent_power_l3_va, { unit: 'VA', deviceClass: 'apparent_power', stateClass: 'measurement' }),
  sensor('active_reactive_power_var', 'Reactive power', (snapshotArg) => snapshotArg.measurement.reactive_power_var, { unit: 'var', deviceClass: 'reactive_power', stateClass: 'measurement' }),
  sensor('active_reactive_power_l1_var', 'Reactive power phase 1', (snapshotArg) => snapshotArg.measurement.reactive_power_l1_var, { unit: 'var', deviceClass: 'reactive_power', stateClass: 'measurement' }),
  sensor('active_reactive_power_l2_var', 'Reactive power phase 2', (snapshotArg) => snapshotArg.measurement.reactive_power_l2_var, { unit: 'var', deviceClass: 'reactive_power', stateClass: 'measurement' }),
  sensor('active_reactive_power_l3_var', 'Reactive power phase 3', (snapshotArg) => snapshotArg.measurement.reactive_power_l3_var, { unit: 'var', deviceClass: 'reactive_power', stateClass: 'measurement' }),
  sensor('active_power_factor', 'Power factor', (snapshotArg) => percentage(snapshotArg.measurement.power_factor), { unit: '%', deviceClass: 'power_factor', stateClass: 'measurement' }),
  sensor('active_power_factor_l1', 'Power factor phase 1', (snapshotArg) => percentage(snapshotArg.measurement.power_factor_l1), { unit: '%', deviceClass: 'power_factor', stateClass: 'measurement' }),
  sensor('active_power_factor_l2', 'Power factor phase 2', (snapshotArg) => percentage(snapshotArg.measurement.power_factor_l2), { unit: '%', deviceClass: 'power_factor', stateClass: 'measurement' }),
  sensor('active_power_factor_l3', 'Power factor phase 3', (snapshotArg) => percentage(snapshotArg.measurement.power_factor_l3), { unit: '%', deviceClass: 'power_factor', stateClass: 'measurement' }),
  sensor('voltage_sag_l1_count', 'Voltage sags detected phase 1', (snapshotArg) => snapshotArg.measurement.voltage_sag_l1_count, { entityCategory: 'diagnostic' }),
  sensor('voltage_sag_l2_count', 'Voltage sags detected phase 2', (snapshotArg) => snapshotArg.measurement.voltage_sag_l2_count, { entityCategory: 'diagnostic' }),
  sensor('voltage_sag_l3_count', 'Voltage sags detected phase 3', (snapshotArg) => snapshotArg.measurement.voltage_sag_l3_count, { entityCategory: 'diagnostic' }),
  sensor('voltage_swell_l1_count', 'Voltage swells detected phase 1', (snapshotArg) => snapshotArg.measurement.voltage_swell_l1_count, { entityCategory: 'diagnostic' }),
  sensor('voltage_swell_l2_count', 'Voltage swells detected phase 2', (snapshotArg) => snapshotArg.measurement.voltage_swell_l2_count, { entityCategory: 'diagnostic' }),
  sensor('voltage_swell_l3_count', 'Voltage swells detected phase 3', (snapshotArg) => snapshotArg.measurement.voltage_swell_l3_count, { entityCategory: 'diagnostic' }),
  sensor('any_power_fail_count', 'Power failures detected', (snapshotArg) => snapshotArg.measurement.any_power_fail_count, { entityCategory: 'diagnostic' }),
  sensor('long_power_fail_count', 'Long power failures detected', (snapshotArg) => snapshotArg.measurement.long_power_fail_count, { entityCategory: 'diagnostic' }),
  sensor('active_power_average_w', 'Average demand', (snapshotArg) => snapshotArg.measurement.average_power_15m_w, { unit: 'W', deviceClass: 'power' }),
  sensor('monthly_power_peak_w', 'Peak demand current month', (snapshotArg) => snapshotArg.measurement.monthly_power_peak_w, { unit: 'W', deviceClass: 'power' }),
  sensor('active_liter_lpm', 'Water usage', (snapshotArg) => snapshotArg.measurement.active_liter_lpm, { unit: 'L/min', deviceClass: 'volume_flow_rate', stateClass: 'measurement' }),
  sensor('total_liter_m3', 'Total water usage', (snapshotArg) => snapshotArg.measurement.total_liter_m3, { unit: 'm3', deviceClass: 'water', stateClass: 'total_increasing' }),
  sensor('state_of_charge_pct', 'State of charge', (snapshotArg) => snapshotArg.measurement.state_of_charge_pct, { unit: '%', deviceClass: 'battery', stateClass: 'measurement' }),
  sensor('cycles', 'Battery cycles', (snapshotArg) => snapshotArg.measurement.cycles, { stateClass: 'total_increasing', entityCategory: 'diagnostic' }),
  sensor('uptime', 'Uptime', (snapshotArg) => uptimeIso(snapshotArg.system?.uptime_s), { deviceClass: 'timestamp', entityCategory: 'diagnostic' }),
];

export class HomeWizardMapper {
  public static toSnapshot(optionsArg: IHomeWizardSnapshotOptions): IHomeWizardSnapshot {
    if (optionsArg.config.snapshot && !optionsArg.rawData) {
      return this.normalizeSnapshot(this.clone(optionsArg.config.snapshot), optionsArg.config, optionsArg.source || 'snapshot', optionsArg.error);
    }

    const rawData = this.rawData(optionsArg.config, optionsArg.rawData);
    const device = this.device(optionsArg.config, rawData.device);
    const endpoint = parseEndpoint(optionsArg.config.url || optionsArg.config.host, optionsArg.config.port, optionsArg.config.protocol, optionsArg.config.apiVersion);
    const apiVersion = this.apiVersion(optionsArg.config, device, endpoint?.protocol);
    const measurement = this.measurement(rawData.measurement || {});
    const state = objectValue(rawData.state) as IHomeWizardStateSnapshot | undefined;
    const system = this.system(rawData.system, measurement, state);
    const batteries = objectValue(rawData.batteries) as IHomeWizardBatteriesSnapshot | undefined;
    const externalDevices = this.externalDevices(measurement);
    measurement.externalDevices = externalDevices;
    const online = optionsArg.online ?? optionsArg.config.online ?? Boolean(rawData.device || rawData.measurement || rawData.system || rawData.state || rawData.batteries);
    const source = optionsArg.source || (rawData.device || rawData.measurement ? 'http' : 'runtime');
    const snapshot: IHomeWizardSnapshot = {
      device,
      host: endpoint?.host,
      port: endpoint?.port,
      protocol: endpoint?.protocol,
      apiVersion,
      measurement,
      system,
      state,
      batteries,
      externalDevices,
      capabilities: this.capabilities(optionsArg.config, device, apiVersion, state, system, batteries, endpoint?.host),
      rawData: Object.keys(rawData).length ? rawData : undefined,
      online,
      updatedAt: rawData.fetchedAt || new Date().toISOString(),
      source,
      error: optionsArg.error,
    };
    return this.normalizeSnapshot(snapshot, optionsArg.config, source, optionsArg.error);
  }

  public static toDevices(snapshotArg: IHomeWizardSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const features = this.toEntities(snapshotArg)
      .filter((entityArg) => entityArg.deviceId === this.mainDeviceId(snapshotArg))
      .map((entityArg) => ({
        id: String(entityArg.attributes?.key || entityArg.uniqueId),
        capability: deviceCapability(entityArg.platform),
        name: entityArg.name,
        readable: true,
        writable: ['switch', 'number', 'select', 'button'].includes(entityArg.platform),
      }));
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [{
      id: this.mainDeviceId(snapshotArg),
      integrationDomain: homeWizardDomain,
      name: snapshotArg.device.productName,
      protocol: snapshotArg.host ? 'http' : 'unknown',
      manufacturer: homeWizardDisplayName,
      model: snapshotArg.device.modelName || snapshotArg.device.productType,
      online: snapshotArg.online,
      features: [
        { id: 'online', capability: 'sensor', name: 'Online', readable: true, writable: false },
        ...features,
      ],
      state: [
        { featureId: 'online', value: snapshotArg.online, updatedAt },
        ...features.map((featureArg) => ({ featureId: featureArg.id, value: deviceStateValue(this.featureState(snapshotArg, featureArg.id)), updatedAt })),
      ],
      metadata: cleanAttributes({
        serial: snapshotArg.device.serial,
        productType: snapshotArg.device.productType,
        apiVersion: snapshotArg.device.apiVersion,
        firmwareVersion: snapshotArg.device.firmwareVersion,
        verificationId: snapshotArg.device.verificationId,
        host: snapshotArg.host,
        port: snapshotArg.port,
        source: snapshotArg.source,
        error: snapshotArg.error,
      }),
    }];

    for (const externalDevice of snapshotArg.externalDevices) {
      devices.push({
        id: this.externalDeviceId(snapshotArg, externalDevice),
        integrationDomain: homeWizardDomain,
        name: externalDeviceName(externalDevice.type),
        protocol: snapshotArg.host ? 'http' : 'unknown',
        manufacturer: homeWizardDisplayName,
        model: snapshotArg.device.productType,
        online: snapshotArg.online,
        features: [{ id: 'value', capability: 'sensor', name: 'Value', readable: true, writable: false }],
        state: [{ featureId: 'value', value: deviceStateValue(externalDevice.value), updatedAt }],
        metadata: cleanAttributes({
          viaDevice: this.mainDeviceId(snapshotArg),
          serial: externalDevice.uniqueId,
          type: externalDevice.type,
          unit: externalDevice.unit,
          timestamp: externalDevice.timestamp,
        }),
      });
    }

    return devices;
  }

  public static toEntities(snapshotArg: IHomeWizardSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    for (const definition of sensorDefinitions) {
      const state = definition.value(snapshotArg);
      if (state !== undefined && state !== null) {
        entities.push(this.entity(snapshotArg, {
          platform: 'sensor',
          key: definition.key,
          name: definition.name,
          state,
          available: snapshotArg.online,
          attributes: cleanAttributes({
            key: definition.key,
            nativeUnitOfMeasurement: definition.unit,
            deviceClass: definition.deviceClass,
            stateClass: definition.stateClass,
            entityCategory: definition.entityCategory,
          }),
        }));
      }
    }

    if (productionPowerProductTypes.has(snapshotArg.device.productType) && numberValue(snapshotArg.measurement.power_w) !== undefined) {
      const power = numberValue(snapshotArg.measurement.power_w);
      entities.push(this.entity(snapshotArg, {
        platform: 'sensor',
        key: 'active_production_power_w',
        name: 'Production power',
        state: power === undefined ? undefined : power * -1,
        available: snapshotArg.online,
        attributes: { key: 'active_production_power_w', nativeUnitOfMeasurement: 'W', deviceClass: 'power', stateClass: 'measurement' },
      }));
    }

    if (snapshotArg.capabilities.powerSwitch) {
      entities.push(this.entity(snapshotArg, {
        platform: 'switch',
        key: 'power_on',
        name: `${snapshotArg.device.productName} Power`,
        state: onOffState(snapshotArg.state?.power_on),
        available: snapshotArg.online && Boolean(snapshotArg.state) && snapshotArg.state?.switch_lock !== true,
        attributes: { key: 'power_on', deviceClass: 'outlet' },
      }));
      entities.push(this.entity(snapshotArg, {
        platform: 'switch',
        key: 'switch_lock',
        name: `${snapshotArg.device.productName} Switch lock`,
        state: onOffState(snapshotArg.state?.switch_lock),
        available: snapshotArg.online && Boolean(snapshotArg.state),
        attributes: { key: 'switch_lock', entityCategory: 'config' },
      }));
    }

    if (snapshotArg.capabilities.cloudConnection) {
      entities.push(this.entity(snapshotArg, {
        platform: 'switch',
        key: 'cloud_connection',
        name: `${snapshotArg.device.productName} Cloud connection`,
        state: onOffState(snapshotArg.system?.cloud_enabled),
        available: snapshotArg.online && Boolean(snapshotArg.system),
        attributes: { key: 'cloud_connection', entityCategory: 'config' },
      }));
    }

    if (snapshotArg.capabilities.statusLedBrightness) {
      entities.push(this.entity(snapshotArg, {
        platform: 'number',
        key: 'status_light_brightness',
        name: `${snapshotArg.device.productName} Status light brightness`,
        state: roundOrUndefined(snapshotArg.system?.status_led_brightness_pct),
        available: snapshotArg.online && Boolean(snapshotArg.system),
        attributes: { key: 'status_light_brightness', nativeUnitOfMeasurement: '%', entityCategory: 'config' },
      }));
    }

    if (snapshotArg.capabilities.batteries && snapshotArg.batteries) {
      entities.push(this.entity(snapshotArg, {
        platform: 'select',
        key: 'battery_group_mode',
        name: `${snapshotArg.device.productName} Battery group mode`,
        state: stringValue(snapshotArg.batteries.mode) || 'unknown',
        available: snapshotArg.online,
        attributes: { key: 'battery_group_mode', options: this.supportedBatteryModes(snapshotArg.device), entityCategory: 'config' },
      }));
    }

    if (snapshotArg.capabilities.identify) {
      entities.push(this.entity(snapshotArg, {
        platform: 'button',
        key: 'identify',
        name: `${snapshotArg.device.productName} Identify`,
        state: 'available',
        available: snapshotArg.online,
        attributes: { key: 'identify', entityCategory: 'config', deviceClass: 'identify' },
      }));
    }

    for (const externalDevice of snapshotArg.externalDevices) {
      const unit = externalDevice.unit === 'm3' ? 'm3' : externalDevice.unit;
      entities.push(this.entity(snapshotArg, {
        platform: 'sensor',
        key: `external_${externalDevice.id}`,
        name: externalDeviceName(externalDevice.type),
        state: externalDevice.value,
        available: snapshotArg.online,
        deviceId: this.externalDeviceId(snapshotArg, externalDevice),
        attributes: cleanAttributes({ key: `external_${externalDevice.id}`, nativeUnitOfMeasurement: unit, externalType: externalDevice.type, timestamp: externalDevice.timestamp }),
      }));
    }

    return entities;
  }

  public static commandForService(snapshotArg: IHomeWizardSnapshot, requestArg: IServiceCallRequest): IHomeWizardCommandRequest | undefined {
    if (requestArg.domain === homeWizardDomain) {
      if (requestArg.service === 'identify') {
        return { action: 'identify', service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
      if (requestArg.service === 'reboot') {
        return { action: 'reboot', service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
      if (requestArg.service === 'telegram') {
        return { action: 'telegram', service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
      if (['request_token', 'get_token', 'authorize'].includes(requestArg.service)) {
        return { action: 'request_token', tokenName: stringValue(requestArg.data?.name) || stringValue(requestArg.data?.tokenName), service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
      if (requestArg.service === 'set_state') {
        return {
          action: 'set_state',
          powerOn: booleanValue(requestArg.data?.power_on ?? requestArg.data?.powerOn),
          switchLock: booleanValue(requestArg.data?.switch_lock ?? requestArg.data?.switchLock),
          brightness: numberValue(requestArg.data?.brightness),
          service: `${requestArg.domain}.${requestArg.service}`,
          target: requestArg.target,
          data: requestArg.data,
        };
      }
      if (requestArg.service === 'set_system') {
        return {
          action: 'set_system',
          cloudEnabled: booleanValue(requestArg.data?.cloud_enabled ?? requestArg.data?.cloudEnabled),
          statusLedBrightnessPct: numberValue(requestArg.data?.status_led_brightness_pct ?? requestArg.data?.statusLedBrightnessPct),
          apiV1Enabled: booleanValue(requestArg.data?.api_v1_enabled ?? requestArg.data?.apiV1Enabled),
          service: `${requestArg.domain}.${requestArg.service}`,
          target: requestArg.target,
          data: requestArg.data,
        };
      }
      if (['set_battery_mode', 'select_battery_mode'].includes(requestArg.service)) {
        return { action: 'set_battery_mode', batteryMode: stringValue(requestArg.data?.mode ?? requestArg.data?.option), service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
    }

    const entity = this.targetEntity(snapshotArg, requestArg);
    const key = stringValue(entity?.attributes?.key);
    if (!key) {
      return undefined;
    }

    if (requestArg.domain === 'switch' && ['turn_on', 'turn_off'].includes(requestArg.service)) {
      const active = requestArg.service === 'turn_on';
      if (key === 'power_on') {
        return { action: 'set_state', powerOn: active, service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
      if (key === 'switch_lock') {
        return { action: 'set_state', switchLock: active, service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
      if (key === 'cloud_connection') {
        return { action: 'set_system', cloudEnabled: active, service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
      }
    }

    if (requestArg.domain === 'number' && ['set_value', 'set_native_value'].includes(requestArg.service) && key === 'status_light_brightness') {
      return { action: 'set_system', statusLedBrightnessPct: numberValue(requestArg.data?.value ?? requestArg.data?.native_value), service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
    }

    if (requestArg.domain === 'select' && requestArg.service === 'select_option' && key === 'battery_group_mode') {
      return { action: 'set_battery_mode', batteryMode: stringValue(requestArg.data?.option), service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
    }

    if (requestArg.domain === 'button' && requestArg.service === 'press' && key === 'identify') {
      return { action: 'identify', service: `${requestArg.domain}.${requestArg.service}`, target: requestArg.target, data: requestArg.data };
    }

    return undefined;
  }

  public static mainDeviceId(snapshotArg: IHomeWizardSnapshot): string {
    return `${homeWizardDomain}.${slug(snapshotArg.device.serial || snapshotArg.device.id || snapshotArg.host || 'device')}`;
  }

  public static supportedBatteryModes(deviceArg: IHomeWizardDeviceSnapshot): string[] {
    const modes = ['standby', 'to_full', 'zero'];
    if (apiMajor(deviceArg.apiVersion) >= 2 && compareApiVersion(deviceArg.apiVersion, '2.2.0') >= 0) {
      modes.push('zero_charge_only', 'zero_discharge_only');
    }
    return modes;
  }

  public static clone<TValue>(valueArg: TValue): TValue {
    return JSON.parse(JSON.stringify(valueArg)) as TValue;
  }

  private static normalizeSnapshot(snapshotArg: IHomeWizardSnapshot, configArg: IHomeWizardConfig, sourceArg: THomeWizardSnapshotSource, errorArg?: string): IHomeWizardSnapshot {
    const rawData = snapshotArg.rawData ? this.clone(snapshotArg.rawData) : undefined;
    return {
      ...snapshotArg,
      host: snapshotArg.host || parseEndpoint(configArg.url || configArg.host, configArg.port, configArg.protocol, configArg.apiVersion)?.host,
      port: snapshotArg.port || parseEndpoint(configArg.url || configArg.host, configArg.port, configArg.protocol, configArg.apiVersion)?.port,
      protocol: snapshotArg.protocol || parseEndpoint(configArg.url || configArg.host, configArg.port, configArg.protocol, configArg.apiVersion)?.protocol,
      source: sourceArg,
      error: errorArg || snapshotArg.error,
      rawData,
      capabilities: this.capabilities(configArg, snapshotArg.device, snapshotArg.apiVersion, snapshotArg.state, snapshotArg.system, snapshotArg.batteries, snapshotArg.host || configArg.host || configArg.url),
    };
  }

  private static rawData(configArg: IHomeWizardConfig, rawDataArg?: Partial<IHomeWizardRawData>): IHomeWizardRawData {
    const rawData: IHomeWizardRawData = this.clone({ ...(configArg.rawData || {}), ...(rawDataArg || {}) });
    if (!rawData.device && (configArg.productType || configArg.productName || configArg.serial || configArg.name)) {
      rawData.device = cleanAttributes({
        product_type: configArg.productType,
        product_name: configArg.productName || configArg.name,
        serial: configArg.serial || configArg.uniqueId,
        api_version: configArg.apiVersion === 'v2' ? '2.0.0' : 'v1',
      });
    }
    return rawData;
  }

  private static device(configArg: IHomeWizardConfig, deviceArg?: Record<string, unknown>): IHomeWizardDeviceSnapshot {
    const productType = stringValue(deviceArg?.product_type ?? deviceArg?.productType) || configArg.productType || 'unknown';
    const serial = stringValue(deviceArg?.serial) || configArg.serial || configArg.uniqueId || endpointHost(configArg.url || configArg.host) || productType;
    const productName = stringValue(deviceArg?.product_name ?? deviceArg?.productName) || configArg.productName || configArg.name || homeWizardProductNames[productType] || homeWizardDisplayName;
    const apiVersionValue = stringValue(deviceArg?.api_version ?? deviceArg?.apiVersion) || (configArg.apiVersion === 'v2' ? '2.0.0' : 'v1');
    const verificationProductId = homeWizardVerificationIds[productType];
    return {
      id: verificationProductId ? `appliance/${verificationProductId}/${serial}` : `${productType}/${serial}`,
      productName,
      productType,
      serial,
      firmwareVersion: stringValue(deviceArg?.firmware_version ?? deviceArg?.firmwareVersion),
      apiVersion: apiVersionValue,
      modelName: homeWizardProductNames[productType] || productName,
      verificationId: verificationProductId ? `appliance/${verificationProductId}/${serial}` : undefined,
    };
  }

  private static apiVersion(configArg: IHomeWizardConfig, deviceArg: IHomeWizardDeviceSnapshot, protocolArg?: THomeWizardProtocol): THomeWizardApiVersion {
    if (configArg.apiVersion && configArg.apiVersion !== 'auto') {
      return configArg.apiVersion;
    }
    if (deviceArg.apiVersion && deviceArg.apiVersion !== 'v1') {
      return 'v2';
    }
    if (configArg.token || configArg.authorizationToken || protocolArg === 'https') {
      return 'v2';
    }
    return 'v1';
  }

  private static measurement(measurementArg: Record<string, unknown>): IHomeWizardMeasurementSnapshot {
    const measurement = { ...measurementArg } as IHomeWizardMeasurementSnapshot;
    alias(measurement, 'protocol_version', 'smr_version');
    alias(measurement, 'tariff', 'active_tariff');
    alias(measurement, 'energy_import_kwh', 'total_power_import_kwh', 'total_power_import_t1_kwh');
    alias(measurement, 'energy_import_t1_kwh', 'total_power_import_t1_kwh');
    alias(measurement, 'energy_import_t2_kwh', 'total_power_import_t2_kwh');
    alias(measurement, 'energy_import_t3_kwh', 'total_power_import_t3_kwh');
    alias(measurement, 'energy_import_t4_kwh', 'total_power_import_t4_kwh');
    alias(measurement, 'energy_export_kwh', 'total_power_export_kwh', 'total_power_export_t1_kwh');
    alias(measurement, 'energy_export_t1_kwh', 'total_power_export_t1_kwh');
    alias(measurement, 'energy_export_t2_kwh', 'total_power_export_t2_kwh');
    alias(measurement, 'energy_export_t3_kwh', 'total_power_export_t3_kwh');
    alias(measurement, 'energy_export_t4_kwh', 'total_power_export_t4_kwh');
    alias(measurement, 'power_w', 'active_power_w');
    alias(measurement, 'power_l1_w', 'active_power_l1_w');
    alias(measurement, 'power_l2_w', 'active_power_l2_w');
    alias(measurement, 'power_l3_w', 'active_power_l3_w');
    alias(measurement, 'voltage_v', 'active_voltage_v');
    alias(measurement, 'voltage_l1_v', 'active_voltage_l1_v');
    alias(measurement, 'voltage_l2_v', 'active_voltage_l2_v');
    alias(measurement, 'voltage_l3_v', 'active_voltage_l3_v');
    alias(measurement, 'current_a', 'active_current_a');
    alias(measurement, 'current_l1_a', 'active_current_l1_a');
    alias(measurement, 'current_l2_a', 'active_current_l2_a');
    alias(measurement, 'current_l3_a', 'active_current_l3_a');
    alias(measurement, 'apparent_power_va', 'active_apparent_power_va');
    alias(measurement, 'apparent_power_l1_va', 'active_apparent_power_l1_va');
    alias(measurement, 'apparent_power_l2_va', 'active_apparent_power_l2_va');
    alias(measurement, 'apparent_power_l3_va', 'active_apparent_power_l3_va');
    alias(measurement, 'reactive_power_var', 'active_reactive_power_var');
    alias(measurement, 'reactive_power_l1_var', 'active_reactive_power_l1_var');
    alias(measurement, 'reactive_power_l2_var', 'active_reactive_power_l2_var');
    alias(measurement, 'reactive_power_l3_var', 'active_reactive_power_l3_var');
    alias(measurement, 'power_factor', 'active_power_factor');
    alias(measurement, 'power_factor_l1', 'active_power_factor_l1');
    alias(measurement, 'power_factor_l2', 'active_power_factor_l2');
    alias(measurement, 'power_factor_l3', 'active_power_factor_l3');
    alias(measurement, 'frequency_hz', 'active_frequency_hz');
    alias(measurement, 'average_power_15m_w', 'active_power_average_w');
    alias(measurement, 'monthly_power_peak_w', 'montly_power_peak_w');
    alias(measurement, 'monthly_power_peak_timestamp', 'montly_power_peak_timestamp');
    return measurement;
  }

  private static system(systemArg: Record<string, unknown> | undefined, measurementArg: IHomeWizardMeasurementSnapshot, stateArg?: IHomeWizardStateSnapshot): IHomeWizardSystemSnapshot | undefined {
    const system = objectValue(systemArg) as IHomeWizardSystemSnapshot | undefined;
    const result: IHomeWizardSystemSnapshot = { ...(system || {}) };
    if (result.wifi_ssid === undefined && measurementArg.wifi_ssid !== undefined) {
      result.wifi_ssid = measurementArg.wifi_ssid;
    }
    if (result.wifi_strength_pct === undefined && measurementArg.wifi_strength !== undefined) {
      result.wifi_strength_pct = measurementArg.wifi_strength;
    }
    if (result.wifi_strength_pct === undefined && result.wifi_rssi_db !== undefined) {
      result.wifi_strength_pct = rssiToPercent(numberValue(result.wifi_rssi_db));
    }
    if (result.status_led_brightness_pct === undefined && stateArg?.brightness !== undefined) {
      result.status_led_brightness_pct = Math.round((numberValue(stateArg.brightness) || 0) / 2.55);
    }
    return Object.keys(result).length ? result : undefined;
  }

  private static externalDevices(measurementArg: IHomeWizardMeasurementSnapshot): IHomeWizardExternalDevice[] {
    const external = measurementArg.external || measurementArg.external_devices || measurementArg.externalDevices;
    const items = Array.isArray(external) ? external : external && typeof external === 'object' ? Object.values(external) : [];
    return items
      .map((itemArg) => objectValue(itemArg))
      .filter((itemArg): itemArg is Record<string, unknown> => Boolean(itemArg))
      .map((itemArg) => {
        const uniqueId = decodeHexString(stringValue(itemArg.unique_id ?? itemArg.uniqueId) || 'external');
        const type = stringValue(itemArg.type);
        return {
          id: slug(`${type || 'external'}_${uniqueId}`),
          uniqueId,
          type,
          value: numberValue(itemArg.value) ?? stringValue(itemArg.value),
          unit: stringValue(itemArg.unit),
          timestamp: stringValue(itemArg.timestamp) || numberValue(itemArg.timestamp),
        };
      });
  }

  private static capabilities(configArg: IHomeWizardConfig, deviceArg: IHomeWizardDeviceSnapshot, apiVersionArg: THomeWizardApiVersion, stateArg?: IHomeWizardStateSnapshot, systemArg?: IHomeWizardSystemSnapshot, batteriesArg?: IHomeWizardBatteriesSnapshot, hostArg?: string): IHomeWizardSnapshot['capabilities'] {
    const major = apiMajor(deviceArg.apiVersion);
    const hasLiveTransport = Boolean(configArg.commandExecutor || configArg.client?.execute || (hostArg && (apiVersionArg === 'v1' || configArg.token || configArg.authorizationToken)));
    return {
      localControl: hasLiveTransport,
      apiV1: apiVersionArg === 'v1' || deviceArg.apiVersion === 'v1',
      apiV2: apiVersionArg === 'v2' || major >= 2,
      powerSwitch: deviceArg.productType === homeWizardProductTypes.energySocket,
      switchLock: deviceArg.productType === homeWizardProductTypes.energySocket,
      cloudConnection: deviceArg.productType !== homeWizardProductTypes.battery && Boolean(systemArg || hasLiveTransport),
      statusLedBrightness: deviceArg.productType === homeWizardProductTypes.battery || deviceArg.productType === homeWizardProductTypes.energySocket || (deviceArg.productType === homeWizardProductTypes.p1Meter && major >= 2),
      identify: !identifyUnsupportedProductTypes.has(deviceArg.productType),
      reboot: major >= 2 && deviceArg.productType !== homeWizardProductTypes.battery,
      batteries: batteryManagedProductTypes.has(deviceArg.productType) && Boolean(batteriesArg || hasLiveTransport),
      telegram: deviceArg.productType === homeWizardProductTypes.p1Meter,
    };
  }

  private static entity(snapshotArg: IHomeWizardSnapshot, optionsArg: IHomeWizardEntityOptions): IIntegrationEntity {
    const deviceId = optionsArg.deviceId || this.mainDeviceId(snapshotArg);
    const uniqueId = `${snapshotArg.device.serial}_${optionsArg.key}`;
    return {
      id: `${optionsArg.platform}.${slug(`${snapshotArg.device.productName}_${optionsArg.key}`)}`,
      uniqueId,
      integrationDomain: homeWizardDomain,
      deviceId,
      platform: optionsArg.platform,
      name: optionsArg.name,
      state: optionsArg.state,
      attributes: optionsArg.attributes,
      available: optionsArg.available,
    };
  }

  private static externalDeviceId(snapshotArg: IHomeWizardSnapshot, deviceArg: IHomeWizardExternalDevice): string {
    return `${homeWizardDomain}.external.${slug(`${snapshotArg.device.serial}_${deviceArg.id}`)}`;
  }

  private static targetEntity(snapshotArg: IHomeWizardSnapshot, requestArg: IServiceCallRequest): IIntegrationEntity | undefined {
    const entities = this.toEntities(snapshotArg);
    return entities.find((entityArg) => {
      return entityArg.id === requestArg.target.entityId
        || entityArg.uniqueId === requestArg.target.entityId
        || entityArg.deviceId === requestArg.target.deviceId;
    });
  }

  private static featureState(snapshotArg: IHomeWizardSnapshot, featureIdArg: string): unknown {
    return this.toEntities(snapshotArg).find((entityArg) => entityArg.attributes?.key === featureIdArg || entityArg.uniqueId === featureIdArg)?.state;
  }
}

function sensor(keyArg: string, nameArg: string, valueArg: (snapshotArg: IHomeWizardSnapshot) => unknown, optionsArg: Omit<IHomeWizardSensorDefinition, 'key' | 'name' | 'value'> = {}): IHomeWizardSensorDefinition {
  return { key: keyArg, name: nameArg, value: valueArg, ...optionsArg };
}

function parseEndpoint(valueArg: string | undefined, portArg?: number, protocolArg?: THomeWizardProtocol, apiVersionArg?: THomeWizardApiVersion): { protocol: THomeWizardProtocol; host: string; port: number } | undefined {
  if (!valueArg) {
    return undefined;
  }
  try {
    const defaultProtocol = protocolArg || (apiVersionArg === 'v2' ? 'https' : 'http');
    const url = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(valueArg) ? valueArg : `${defaultProtocol}://${valueArg}`);
    const protocol = url.protocol === 'https:' ? 'https' : 'http';
    return { protocol, host: url.hostname, port: portArg || (url.port ? Number(url.port) : protocol === 'https' ? homeWizardDefaultHttpsPort : homeWizardDefaultHttpPort) };
  } catch {
    const protocol = protocolArg || (apiVersionArg === 'v2' ? 'https' : 'http');
    return { protocol, host: valueArg, port: portArg || (protocol === 'https' ? homeWizardDefaultHttpsPort : homeWizardDefaultHttpPort) };
  }
}

function endpointHost(valueArg: string | undefined): string | undefined {
  return parseEndpoint(valueArg)?.host;
}

function alias(targetArg: Record<string, unknown>, canonicalArg: string, ...sourceArgs: string[]): void {
  if (targetArg[canonicalArg] !== undefined) {
    return;
  }
  for (const source of sourceArgs) {
    if (targetArg[source] !== undefined) {
      targetArg[canonicalArg] = targetArg[source];
      return;
    }
  }
}

function apiMajor(versionArg: string | undefined): number {
  if (!versionArg || versionArg === 'v1') {
    return 1;
  }
  const match = versionArg.match(/(\d+)/);
  return match ? Number(match[1]) : 1;
}

function compareApiVersion(versionArg: string, otherArg: string): number {
  const left = versionArg.replace(/^v/i, '').split('.').map((partArg) => Number(partArg) || 0);
  const right = otherArg.replace(/^v/i, '').split('.').map((partArg) => Number(partArg) || 0);
  for (let index = 0; index < Math.max(left.length, right.length); index++) {
    const diff = (left[index] || 0) - (right[index] || 0);
    if (diff !== 0) {
      return diff;
    }
  }
  return 0;
}

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

function valueToString(valueArg: unknown): string | undefined {
  if (valueArg === undefined || valueArg === null) {
    return undefined;
  }
  return String(valueArg);
}

function 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;
}

function booleanValue(valueArg: unknown): boolean | undefined {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'number') {
    return valueArg !== 0;
  }
  if (typeof valueArg === 'string') {
    const normalized = valueArg.toLowerCase();
    if (['true', '1', 'on', 'yes'].includes(normalized)) {
      return true;
    }
    if (['false', '0', 'off', 'no'].includes(normalized)) {
      return false;
    }
  }
  return undefined;
}

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

function cleanAttributes<TValue extends Record<string, unknown>>(valueArg: TValue): TValue {
  return Object.fromEntries(Object.entries(valueArg).filter(([, value]) => value !== undefined && value !== null && value !== '')) as TValue;
}

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

function percentage(valueArg: unknown): number | undefined {
  const value = numberValue(valueArg);
  return value === undefined ? undefined : value * 100;
}

function uptimeIso(valueArg: unknown): string | undefined {
  const seconds = numberValue(valueArg);
  return seconds === undefined ? undefined : new Date(Date.now() - seconds * 1000).toISOString();
}

function onOffState(valueArg: unknown): 'on' | 'off' | 'unknown' {
  const value = booleanValue(valueArg);
  return value === undefined ? 'unknown' : value ? 'on' : 'off';
}

function roundOrUndefined(valueArg: unknown): number | undefined {
  const value = numberValue(valueArg);
  return value === undefined ? undefined : Math.round(value);
}

function rssiToPercent(valueArg: number | undefined): number | undefined {
  if (valueArg === undefined) {
    return undefined;
  }
  if (valueArg <= -100 || valueArg === 0) {
    return 0;
  }
  if (valueArg >= -50) {
    return 100;
  }
  return 2 * (valueArg + 100);
}

function decodeHexString(valueArg: string): string {
  if (!/^[0-9a-f]+$/i.test(valueArg) || valueArg.length % 2 !== 0) {
    return valueArg;
  }
  try {
    const decoded = Buffer.from(valueArg, 'hex').toString('utf8');
    return decoded.replace(/[\u0000-\u001f\u007f]/g, '') || valueArg;
  } catch {
    return valueArg;
  }
}

function externalDeviceName(typeArg: string | undefined): string {
  switch (typeArg) {
    case 'gas_meter':
      return 'Gas meter';
    case 'heat_meter':
      return 'Heat meter';
    case 'warm_water_meter':
      return 'Warm water meter';
    case 'water_meter':
      return 'Water meter';
    case 'inlet_heat_meter':
      return 'Inlet heat meter';
    default:
      return 'External meter';
  }
}

function deviceCapability(platformArg: TEntityPlatform): plugins.shxInterfaces.data.TDeviceCapability {
  switch (platformArg) {
    case 'switch':
      return 'switch';
    case 'light':
      return 'light';
    case 'cover':
      return 'cover';
    case 'fan':
      return 'fan';
    case 'climate':
      return 'climate';
    case 'media_player':
      return 'media';
    default:
      return 'sensor';
  }
}

function deviceStateValue(valueArg: unknown): plugins.shxInterfaces.data.TDeviceStateValue {
  if (valueArg === undefined || valueArg === null) {
    return null;
  }
  if (['string', 'number', 'boolean'].includes(typeof valueArg)) {
    return valueArg as string | number | boolean;
  }
  if (typeof valueArg === 'object' && !Array.isArray(valueArg)) {
    return valueArg as Record<string, unknown>;
  }
  return String(valueArg);
}
