import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type {
  IFroniusConfig,
  IFroniusCommandRequest,
  IFroniusDeviceSnapshot,
  IFroniusInverterSnapshot,
  IFroniusLoggerSnapshot,
  IFroniusRawData,
  IFroniusSensorSnapshot,
  IFroniusSnapshot,
  TFroniusDeviceKind,
  TFroniusProtocol,
  TFroniusSnapshotSource,
} from './fronius.types.js';
import { froniusDefaultHttpPort, froniusDefaultHttpsPort, froniusDisplayName, froniusDomain, froniusManufacturer } from './fronius.types.js';

interface IFroniusSnapshotOptions {
  config: IFroniusConfig;
  rawData?: Partial<IFroniusRawData>;
  online?: boolean;
  source?: TFroniusSnapshotSource;
  error?: string;
}

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

interface IEndpointInfo {
  protocol?: TFroniusProtocol;
  host?: string;
  port?: number;
}

const inverterDeviceTypes: Record<number, { manufacturer: string; model: string }> = {
  1: { manufacturer: froniusManufacturer, model: 'Gen24' },
  72: { manufacturer: froniusManufacturer, model: 'Eco 27.0-3-S' },
  73: { manufacturer: froniusManufacturer, model: 'Eco 25.0-3-S' },
  75: { manufacturer: froniusManufacturer, model: 'Primo 6.0-1' },
  76: { manufacturer: froniusManufacturer, model: 'Primo 5.0-1' },
  77: { manufacturer: froniusManufacturer, model: 'Primo 4.6-1' },
  78: { manufacturer: froniusManufacturer, model: 'Primo 4.0-1' },
  79: { manufacturer: froniusManufacturer, model: 'Primo 3.6-1' },
  80: { manufacturer: froniusManufacturer, model: 'Primo 3.5-1' },
  81: { manufacturer: froniusManufacturer, model: 'Primo 3.0-1' },
  82: { manufacturer: froniusManufacturer, model: 'Symo Hybrid 4.0-3-S' },
  83: { manufacturer: froniusManufacturer, model: 'Symo Hybrid 3.0-3-S' },
  99: { manufacturer: froniusManufacturer, model: 'Symo Hybrid 5.0-3-S' },
  105: { manufacturer: froniusManufacturer, model: 'Symo 7.0-3-M' },
  110: { manufacturer: froniusManufacturer, model: 'Symo 6.0-3-M' },
  111: { manufacturer: froniusManufacturer, model: 'Symo 4.5-3-M' },
  112: { manufacturer: froniusManufacturer, model: 'Symo 3.7-3-M' },
  113: { manufacturer: froniusManufacturer, model: 'Symo 3.0-3-M' },
  121: { manufacturer: froniusManufacturer, model: 'Symo 20.0-3-M' },
  122: { manufacturer: froniusManufacturer, model: 'Symo 5.0-3-M' },
  123: { manufacturer: froniusManufacturer, model: 'Symo 8.2-3-M' },
  124: { manufacturer: froniusManufacturer, model: 'Symo 6.7-3-M' },
  125: { manufacturer: froniusManufacturer, model: 'Symo 5.5-3-M' },
  232: { manufacturer: froniusManufacturer, model: 'Symo 10.0-3-M' },
  233: { manufacturer: froniusManufacturer, model: 'Symo 12.5-3-M' },
};

const sensorNames: Record<string, string> = {
  battery_mode: 'Battery Mode',
  backup_mode: 'Backup Mode',
  battery_standby: 'Battery Standby',
  co2_factor: 'CO2 Factor',
  current_ac: 'AC Current',
  current_ac_phase_1: 'AC Current Phase 1',
  current_ac_phase_2: 'AC Current Phase 2',
  current_ac_phase_3: 'AC Current Phase 3',
  current_dc: 'DC Current',
  energy_day: 'Energy Today',
  energy_total: 'Energy Total',
  energy_year: 'Energy Year',
  energy_real_consumed: 'Energy Consumed',
  energy_real_produced: 'Energy Produced',
  error_code: 'Error Code',
  error_message: 'Error Message',
  frequency_ac: 'AC Frequency',
  frequency_phase_average: 'Frequency Phase Average',
  hardware_platform: 'Hardware Platform',
  hardware_version: 'Hardware Version',
  inverter_state: 'Inverter State',
  led_color: 'LED Color',
  led_state: 'LED State',
  meter_location: 'Meter Location',
  meter_mode: 'Meter Mode',
  power_ac: 'AC Power',
  power_apparent: 'Apparent Power',
  power_battery: 'Battery Power',
  power_grid: 'Grid Power',
  power_load: 'Load Power',
  power_photovoltaics: 'Photovoltaic Power',
  power_reactive: 'Reactive Power',
  power_real: 'Real Power',
  product_type: 'Product Type',
  relative_autonomy: 'Relative Autonomy',
  relative_self_consumption: 'Relative Self Consumption',
  software_version: 'Software Version',
  state_of_charge: 'State Of Charge',
  status_code: 'Status Code',
  time_zone: 'Time Zone',
  time_zone_location: 'Time Zone Location',
  timestamp: 'Timestamp',
  unique_identifier: 'Unique Identifier',
  voltage_ac: 'AC Voltage',
  voltage_ac_phase_1: 'AC Voltage Phase 1',
  voltage_ac_phase_2: 'AC Voltage Phase 2',
  voltage_ac_phase_3: 'AC Voltage Phase 3',
  voltage_dc: 'DC Voltage',
};

const powerFlowMap: Record<string, string> = {
  BackupMode: 'backup_mode',
  BatteryStandby: 'battery_standby',
  E_Day: 'energy_day',
  E_Total: 'energy_total',
  E_Year: 'energy_year',
  Meter_Location: 'meter_location',
  Mode: 'meter_mode',
  P_Akku: 'power_battery',
  P_Grid: 'power_grid',
  P_Load: 'power_load',
  P_PV: 'power_photovoltaics',
  rel_Autonomy: 'relative_autonomy',
  rel_SelfConsumption: 'relative_self_consumption',
};

const inverterDataMap: Record<string, string> = {
  DAY_ENERGY: 'energy_day',
  TOTAL_ENERGY: 'energy_total',
  YEAR_ENERGY: 'energy_year',
  FAC: 'frequency_ac',
  IAC: 'current_ac',
  IDC: 'current_dc',
  PAC: 'power_ac',
  UAC: 'voltage_ac',
  UDC: 'voltage_dc',
};

const meterDataMap: Record<string, string> = {
  Current_AC_Phase_1: 'current_ac_phase_1',
  ACBRIDGE_CURRENT_ACTIVE_MEAN_01_F32: 'current_ac_phase_1',
  Current_AC_Phase_2: 'current_ac_phase_2',
  ACBRIDGE_CURRENT_ACTIVE_MEAN_02_F32: 'current_ac_phase_2',
  Current_AC_Phase_3: 'current_ac_phase_3',
  ACBRIDGE_CURRENT_ACTIVE_MEAN_03_F32: 'current_ac_phase_3',
  EnergyReal_WAC_Sum_Consumed: 'energy_real_consumed',
  SMARTMETER_ENERGYACTIVE_CONSUMED_SUM_F64: 'energy_real_consumed',
  EnergyReal_WAC_Sum_Produced: 'energy_real_produced',
  SMARTMETER_ENERGYACTIVE_PRODUCED_SUM_F64: 'energy_real_produced',
  Frequency_Phase_Average: 'frequency_phase_average',
  PowerApparent_S_Sum: 'power_apparent',
  PowerReactive_Q_Sum: 'power_reactive',
  PowerReal_P_Sum: 'power_real',
  Voltage_AC_Phase_1: 'voltage_ac_phase_1',
  Voltage_AC_Phase_2: 'voltage_ac_phase_2',
  Voltage_AC_Phase_3: 'voltage_ac_phase_3',
  Meter_Location_Current: 'meter_location',
};

export class FroniusMapper {
  public static toSnapshot(optionsArg: IFroniusSnapshotOptions): IFroniusSnapshot {
    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 endpoint = parseEndpoint(optionsArg.config.url || optionsArg.config.host, optionsArg.config.port, optionsArg.config.protocol);
    const loggerInfo = this.loggerInfo(rawData.loggerInfo);
    const inverters = this.inverters(rawData.inverterInfo);
    const system = this.systemDevice(optionsArg.config, rawData, loggerInfo, inverters, endpoint);
    const devices: IFroniusDeviceSnapshot[] = [system];
    const sensors: IFroniusSensorSnapshot[] = [];

    for (const inverter of inverters) {
      inverter.viaDeviceId = system.id;
      devices.push(inverter);
    }

    devices.push(...this.childDevices(rawData.systemMeter, 'meter', 'Meter', system.id));
    devices.push(...this.childDevices(rawData.systemOhmpilot, 'ohmpilot', 'Ohmpilot', system.id));
    devices.push(...this.childDevices(rawData.systemStorage, 'storage', 'Storage', system.id));

    sensors.push(...this.loggerSensors(system, loggerInfo, rawData.loggerInfo, Boolean(optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData))));
    sensors.push(...this.powerFlowSensors(system, rawData.powerFlow, Boolean(optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData))));
    for (const inverter of inverters) {
      sensors.push(...this.inverterSensors(inverter, rawData.inverterDeviceData?.[inverter.solarNetId || inverter.id], Boolean(optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData))));
    }
    sensors.push(...this.systemCollectionSensors(devices, rawData.systemMeter, 'meter', Boolean(optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData))));
    sensors.push(...this.systemCollectionSensors(devices, rawData.systemOhmpilot, 'ohmpilot', Boolean(optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData))));
    sensors.push(...this.systemCollectionSensors(devices, rawData.systemStorage, 'storage', Boolean(optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData))));

    const online = optionsArg.online ?? optionsArg.config.online ?? this.hasUsableRawData(rawData);
    const source = optionsArg.source || (rawData.loggerInfo || rawData.inverterInfo || rawData.powerFlow || rawData.systemMeter ? 'http' : 'runtime');
    return this.normalizeSnapshot({
      system,
      devices,
      inverters,
      sensors,
      loggerInfo,
      host: endpoint.host,
      port: endpoint.port,
      protocol: endpoint.protocol,
      apiVersion: this.apiVersion(optionsArg.config, rawData),
      rawData: this.hasUsableRawData(rawData) ? rawData : undefined,
      capabilities: {
        localRead: Boolean(optionsArg.config.client || endpoint.host || optionsArg.config.snapshot || optionsArg.config.rawData || this.hasUsableRawData(rawData)),
        localControl: Boolean(optionsArg.config.commandExecutor || optionsArg.config.client?.execute),
        readOnlySolarApi: true,
      },
      online,
      updatedAt: rawData.fetchedAt || new Date().toISOString(),
      source,
      error: optionsArg.error,
    }, optionsArg.config, source, optionsArg.error);
  }

  public static toDevices(snapshotArg: IFroniusSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    return snapshotArg.devices.map((deviceArg) => {
      const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
      const deviceSensors = snapshotArg.sensors.filter((sensorArg) => sensorArg.deviceId === deviceArg.id);
      const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
        { id: 'online', capability: 'sensor', name: 'Online', readable: true, writable: false },
        ...deviceSensors.map((sensorArg) => ({
          id: sensorArg.id,
          capability: sensorArg.deviceClass === 'energy' ? 'energy' as const : 'sensor' as const,
          name: sensorArg.name,
          readable: true,
          writable: false,
          unit: sensorArg.unit,
        })),
      ];
      return {
        id: deviceArg.id,
        integrationDomain: froniusDomain,
        name: deviceArg.name,
        protocol: snapshotArg.host ? 'http' : 'unknown',
        manufacturer: deviceArg.manufacturer,
        model: deviceArg.model || deviceArg.kind,
        online: snapshotArg.online,
        features,
        state: [
          { featureId: 'online', value: snapshotArg.online, updatedAt },
          ...deviceSensors.map((sensorArg) => ({ featureId: sensorArg.id, value: deviceStateValue(sensorArg.value), updatedAt })),
        ],
        metadata: cleanAttributes({
          ...deviceArg.metadata,
          kind: deviceArg.kind,
          serialNumber: deviceArg.serialNumber,
          uniqueId: deviceArg.uniqueId,
          solarNetId: deviceArg.solarNetId,
          viaDeviceId: deviceArg.viaDeviceId,
          host: snapshotArg.host,
          port: snapshotArg.port,
          source: snapshotArg.source,
          error: snapshotArg.error,
        }),
      };
    });
  }

  public static toEntities(snapshotArg: IFroniusSnapshot): IIntegrationEntity[] {
    const seen = new Map<string, number>();
    return snapshotArg.sensors.map((sensorArg) => {
      const device = snapshotArg.devices.find((deviceArg) => deviceArg.id === sensorArg.deviceId) || snapshotArg.system;
      return this.entity(snapshotArg, {
        platform: 'sensor',
        key: sensorArg.id,
        name: `${device.name} ${sensorArg.name}`,
        state: sensorArg.value ?? null,
        available: sensorArg.available && snapshotArg.online,
        deviceId: sensorArg.deviceId,
        attributes: cleanAttributes({
          ...sensorArg.attributes,
          key: sensorArg.id,
          kind: sensorArg.kind,
          nativeUnitOfMeasurement: sensorArg.unit,
          deviceClass: sensorArg.deviceClass,
          stateClass: sensorArg.stateClass,
          entityCategory: sensorArg.entityCategory,
        }),
      }, seen);
    });
  }

  public static commandForService(snapshotArg: IFroniusSnapshot, requestArg: IServiceCallRequest): IFroniusCommandRequest | undefined {
    void snapshotArg;
    if (requestArg.domain !== froniusDomain) {
      return undefined;
    }
    if (requestArg.service === 'snapshot' || requestArg.service === 'status') {
      return { action: 'snapshot' };
    }
    if (requestArg.service === 'refresh') {
      return { action: 'refresh' };
    }
    return { action: requestArg.service, service: requestArg.service, target: requestArg.target, data: requestArg.data };
  }

  public static hasUsableRawData(rawDataArg: Partial<IFroniusRawData> | undefined): boolean {
    return Boolean(rawDataArg && (rawDataArg.loggerInfo || rawDataArg.inverterInfo || rawDataArg.powerFlow || rawDataArg.systemMeter || rawDataArg.systemOhmpilot || rawDataArg.systemStorage || Object.keys(rawDataArg.inverterDeviceData || {}).length));
  }

  public static inverterDeviceIds(inverterInfoArg: unknown): string[] {
    return this.inverters(inverterInfoArg).map((inverterArg) => inverterArg.solarNetId || inverterArg.id).filter(Boolean);
  }

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

  private static rawData(configArg: IFroniusConfig, rawDataArg?: Partial<IFroniusRawData>): Partial<IFroniusRawData> {
    return {
      ...configArg.rawData,
      ...rawDataArg,
      inverterDeviceData: {
        ...(configArg.rawData?.inverterDeviceData || {}),
        ...(rawDataArg?.inverterDeviceData || {}),
      },
      errors: {
        ...(configArg.rawData?.errors || {}),
        ...(rawDataArg?.errors || {}),
      },
    };
  }

  private static normalizeSnapshot(snapshotArg: IFroniusSnapshot, configArg: IFroniusConfig, sourceArg: TFroniusSnapshotSource, errorArg?: string): IFroniusSnapshot {
    const endpoint = parseEndpoint(configArg.url || configArg.host, configArg.port, configArg.protocol);
    snapshotArg.host = snapshotArg.host || endpoint.host;
    snapshotArg.port = snapshotArg.port || endpoint.port;
    snapshotArg.protocol = snapshotArg.protocol || endpoint.protocol;
    snapshotArg.source = sourceArg;
    snapshotArg.error = errorArg || snapshotArg.error;
    snapshotArg.capabilities = {
      ...snapshotArg.capabilities,
      localRead: snapshotArg.capabilities.localRead || Boolean(configArg.client || endpoint.host || configArg.snapshot || configArg.rawData),
      localControl: Boolean(configArg.commandExecutor || configArg.client?.execute || snapshotArg.capabilities.localControl),
      readOnlySolarApi: true,
    };
    return snapshotArg;
  }

  private static systemDevice(configArg: IFroniusConfig, rawDataArg: Partial<IFroniusRawData>, loggerInfoArg: IFroniusLoggerSnapshot | undefined, invertersArg: IFroniusInverterSnapshot[], endpointArg: IEndpointInfo): IFroniusDeviceSnapshot {
    const uniqueId = configArg.uniqueId || loggerInfoArg?.uniqueIdentifier || invertersArg[0]?.uniqueId || endpointArg.host || 'solar_net';
    const hostLabel = endpointArg.host ? ` at ${endpointArg.host}` : '';
    return {
      id: `fronius.system.${slug(uniqueId)}`,
      kind: 'system',
      name: configArg.name || `SolarNet ${loggerInfoArg ? 'Datalogger' : 'System'}${hostLabel}`,
      manufacturer: froniusManufacturer,
      model: loggerInfoArg?.productType || (configArg.isLogger ? 'Datalogger Web' : froniusDisplayName),
      serialNumber: configArg.serialNumber || loggerInfoArg?.uniqueIdentifier,
      uniqueId,
      metadata: cleanAttributes({
        apiVersion: this.apiVersion(configArg, rawDataArg),
        logger: Boolean(loggerInfoArg),
      }),
    };
  }

  private static loggerInfo(rawArg: unknown): IFroniusLoggerSnapshot | undefined {
    const data = responseData(rawArg);
    if (!data) {
      return undefined;
    }
    return cleanAttributes({
      uniqueIdentifier: stringValue(sensorValue(data.unique_identifier) ?? sensorValue(data.UniqueID)),
      hardwareVersion: stringValue(sensorValue(data.hardware_version) ?? sensorValue(data.HWVersion)),
      softwareVersion: stringValue(sensorValue(data.software_version) ?? sensorValue(data.SWVersion)),
      productType: stringValue(sensorValue(data.product_type) ?? sensorValue(data.ProductID)) || 'Datalogger Web',
      timeZone: stringValue(sensorValue(data.time_zone) ?? sensorValue(data.TimezoneName)),
      raw: rawArg,
    }) as IFroniusLoggerSnapshot;
  }

  private static inverters(rawArg: unknown): IFroniusInverterSnapshot[] {
    const data = responseData(rawArg);
    if (!data) {
      return [];
    }
    const normalized = Array.isArray(data.inverters) ? data.inverters : undefined;
    if (normalized) {
      return normalized.map((inverterArg, indexArg) => this.inverterFromNormalized(inverterArg as Record<string, unknown>, indexArg));
    }
    return Object.entries(data).map(([deviceId, value], indexArg) => this.inverterFromSolarApi(deviceId, objectValue(value) || {}, indexArg));
  }

  private static inverterFromNormalized(inverterArg: Record<string, unknown>, indexArg: number): IFroniusInverterSnapshot {
    const solarNetId = stringValue(sensorValue(inverterArg.device_id)) || String(indexArg + 1);
    const uniqueId = stringValue(sensorValue(inverterArg.unique_id)) || solarNetId;
    const deviceTypeValue = numberValue(sensorValue(inverterArg.device_type));
    const knownType = deviceTypeValue === undefined ? undefined : inverterDeviceTypes[deviceTypeValue];
    return {
      id: `fronius.inverter.${slug(uniqueId || solarNetId)}`,
      kind: 'inverter',
      name: stringValue(sensorValue(inverterArg.custom_name)) || `Inverter ${solarNetId}`,
      manufacturer: stringValue((objectValue(inverterArg.device_type) || {}).manufacturer) || knownType?.manufacturer || froniusManufacturer,
      model: stringValue((objectValue(inverterArg.device_type) || {}).model) || knownType?.model || (deviceTypeValue === undefined ? undefined : `Device Type ${deviceTypeValue}`),
      uniqueId,
      solarNetId,
      pvPower: numberValue(sensorValue(inverterArg.pv_power)),
      statusCode: numberValue(sensorValue(inverterArg.status_code)),
      errorCode: numberValue(sensorValue(inverterArg.error_code)),
      metadata: cleanAttributes({ show: sensorValue(inverterArg.show), deviceType: deviceTypeValue }),
    };
  }

  private static inverterFromSolarApi(deviceIdArg: string, dataArg: Record<string, unknown>, indexArg: number): IFroniusInverterSnapshot {
    const deviceType = numberValue(dataArg.DT);
    const knownType = deviceType === undefined ? undefined : inverterDeviceTypes[deviceType];
    const uniqueId = stringValue(dataArg.UniqueID) || deviceIdArg || String(indexArg + 1);
    return {
      id: `fronius.inverter.${slug(uniqueId)}`,
      kind: 'inverter',
      name: stringValue(dataArg.CustomName) || `Inverter ${deviceIdArg || indexArg + 1}`,
      manufacturer: knownType?.manufacturer || froniusManufacturer,
      model: knownType?.model || (deviceType === undefined ? undefined : `Device Type ${deviceType}`),
      uniqueId,
      solarNetId: deviceIdArg || String(indexArg + 1),
      pvPower: numberValue(dataArg.PVPower),
      statusCode: numberValue(dataArg.StatusCode),
      errorCode: numberValue(dataArg.ErrorCode),
      metadata: cleanAttributes({ show: dataArg.Show, deviceType }),
    };
  }

  private static loggerSensors(deviceArg: IFroniusDeviceSnapshot, loggerInfoArg: IFroniusLoggerSnapshot | undefined, rawArg: unknown, onlineArg: boolean): IFroniusSensorSnapshot[] {
    const data = responseData(rawArg);
    const sensors: IFroniusSensorSnapshot[] = [];
    const entries: Record<string, unknown> = {
      hardware_version: loggerInfoArg?.hardwareVersion,
      software_version: loggerInfoArg?.softwareVersion,
      product_type: loggerInfoArg?.productType,
      time_zone: loggerInfoArg?.timeZone,
      unique_identifier: loggerInfoArg?.uniqueIdentifier,
    };
    if (data) {
      for (const key of ['co2_factor', 'cash_factor', 'delivery_factor', 'hardware_platform', 'time_zone_location', 'utc_offset']) {
        entries[key] = sensorValue(data[key]) ?? sensorValue(data[pascalish(key)]);
      }
    }
    for (const [key, value] of Object.entries(entries)) {
      const sensor = this.sensor(deviceArg.id, 'logger', key, value, onlineArg, { entityCategory: 'diagnostic' });
      if (sensor) {
        sensors.push(sensor);
      }
    }
    return sensors;
  }

  private static powerFlowSensors(deviceArg: IFroniusDeviceSnapshot, rawArg: unknown, onlineArg: boolean): IFroniusSensorSnapshot[] {
    const data = responseData(rawArg);
    if (!data) {
      return [];
    }
    const site = objectValue(data.Site) || data;
    const sensors: IFroniusSensorSnapshot[] = [];
    for (const [rawKey, key] of Object.entries(powerFlowMap)) {
      const value = site[rawKey] ?? sensorValue(site[key]);
      const sensor = this.sensor(deviceArg.id, 'power_flow', key, withUnit(key, value), onlineArg);
      if (sensor) {
        sensors.push(sensor);
      }
    }
    const inverters = objectValue(data.Inverters);
    const inverterOne = objectValue(inverters?.['1']);
    if (inverterOne?.SOC !== undefined) {
      const sensor = this.sensor(deviceArg.id, 'power_flow', 'state_of_charge', { value: inverterOne.SOC, unit: '%' }, onlineArg);
      if (sensor) {
        sensors.push(sensor);
      }
    }
    if (inverterOne?.Battery_Mode !== undefined) {
      const sensor = this.sensor(deviceArg.id, 'power_flow', 'battery_mode', inverterOne.Battery_Mode, onlineArg);
      if (sensor) {
        sensors.push(sensor);
      }
    }
    return sensors;
  }

  private static inverterSensors(deviceArg: IFroniusInverterSnapshot, rawArg: unknown, onlineArg: boolean): IFroniusSensorSnapshot[] {
    const data = responseData(rawArg);
    const values: Record<string, unknown> = cleanAttributes({
      pv_power: deviceArg.pvPower === undefined ? undefined : { value: deviceArg.pvPower, unit: 'W' },
      status_code: deviceArg.statusCode,
      error_code: deviceArg.errorCode,
    });
    if (data) {
      for (const [rawKey, key] of Object.entries(inverterDataMap)) {
        values[key] = solarApiValue(data[rawKey]) ?? sensorValue(data[key]);
      }
      for (let index = 2; index < 10; index++) {
        values[`current_dc_${index}`] = solarApiValue(data[`IDC_${index}`]) ?? sensorValue(data[`current_dc_${index}`]);
        values[`voltage_dc_${index}`] = solarApiValue(data[`UDC_${index}`]) ?? sensorValue(data[`voltage_dc_${index}`]);
      }
      const status = objectValue(data.DeviceStatus);
      values.inverter_state = status?.InverterState ?? sensorValue(data.inverter_state);
      values.error_code = status?.ErrorCode ?? values.error_code;
      values.status_code = status?.StatusCode ?? values.status_code;
      values.led_state = status?.LEDState ?? sensorValue(data.led_state);
      values.led_color = status?.LEDColor ?? sensorValue(data.led_color);
    }
    return Object.entries(values).map(([key, value]) => this.sensor(deviceArg.id, 'inverter', key, value, onlineArg, diagnosticOptions(key))).filter(Boolean) as IFroniusSensorSnapshot[];
  }

  private static childDevices(rawArg: unknown, kindArg: Exclude<TFroniusDeviceKind, 'system' | 'inverter'>, labelArg: string, viaDeviceIdArg: string): IFroniusDeviceSnapshot[] {
    const data = collectionData(rawArg, kindArg);
    return Object.entries(data).map(([deviceId, value]) => {
      const details = objectValue(objectValue(value)?.Details) || objectValue(value) || {};
      const serial = stringValue(details.Serial) || deviceId;
      return {
        id: `fronius.${kindArg}.${slug(serial || deviceId)}`,
        kind: kindArg,
        name: `${labelArg} ${deviceId}`,
        manufacturer: stringValue(details.Manufacturer) || froniusManufacturer,
        model: stringValue(details.Model) || labelArg,
        serialNumber: stringValue(details.Serial),
        solarNetId: deviceId,
        viaDeviceId: viaDeviceIdArg,
      };
    });
  }

  private static systemCollectionSensors(devicesArg: IFroniusDeviceSnapshot[], rawArg: unknown, kindArg: Exclude<TFroniusDeviceKind, 'system' | 'inverter'>, onlineArg: boolean): IFroniusSensorSnapshot[] {
    const data = collectionData(rawArg, kindArg);
    const sensors: IFroniusSensorSnapshot[] = [];
    for (const [deviceId, value] of Object.entries(data)) {
      const device = devicesArg.find((deviceArg) => deviceArg.kind === kindArg && deviceArg.solarNetId === deviceId);
      if (!device) {
        continue;
      }
      const flat = kindArg === 'meter' ? knownMappedValues(objectValue(value) || {}, meterDataMap) : flattenScalars(objectValue(value) || {});
      for (const [key, sensorValueArg] of Object.entries(flat)) {
        const sensor = this.sensor(device.id, kindArg, key, sensorValueArg, onlineArg, diagnosticOptions(key));
        if (sensor) {
          sensors.push(sensor);
        }
      }
    }
    return sensors;
  }

  private static sensor(deviceIdArg: string, kindArg: string, keyArg: string, valueArg: unknown, onlineArg: boolean, optionsArg: { entityCategory?: string } = {}): IFroniusSensorSnapshot | undefined {
    const normalized = normalizeSensorValue(keyArg, valueArg);
    if (normalized.value === undefined) {
      return undefined;
    }
    const unit = normalized.unit;
    const meta = sensorMeta(keyArg, unit);
    return {
      id: keyArg,
      deviceId: deviceIdArg,
      kind: kindArg,
      name: sensorNames[keyArg] || humanName(keyArg),
      value: normalized.value,
      unit,
      deviceClass: meta.deviceClass,
      stateClass: meta.stateClass,
      entityCategory: optionsArg.entityCategory || meta.entityCategory,
      available: onlineArg && normalized.value !== null,
      attributes: cleanAttributes({ sourceKey: normalized.sourceKey }),
    };
  }

  private static apiVersion(configArg: IFroniusConfig, rawDataArg: Partial<IFroniusRawData>): 'auto' | 'v0' | 'v1' {
    if (configArg.apiVersion && configArg.apiVersion !== 'auto') {
      return configArg.apiVersion;
    }
    const value = numberValue(rawDataArg.apiVersion?.APIVersion);
    if (value === 0) {
      return 'v0';
    }
    if (value === 1) {
      return 'v1';
    }
    return configArg.apiVersion || 'auto';
  }

  private static entity(snapshotArg: IFroniusSnapshot, optionsArg: IFroniusEntityOptions, seenArg: Map<string, number>): IIntegrationEntity {
    const device = snapshotArg.devices.find((deviceArg) => deviceArg.id === optionsArg.deviceId) || snapshotArg.system;
    const baseId = `${optionsArg.platform}.${slug(device.name)}_${slug(optionsArg.key)}`;
    const seenCount = seenArg.get(baseId) || 0;
    seenArg.set(baseId, seenCount + 1);
    const id = seenCount ? `${baseId}_${seenCount + 1}` : baseId;
    return {
      id,
      uniqueId: `${froniusDomain}_${slug(device.id)}_${slug(optionsArg.key)}`,
      integrationDomain: froniusDomain,
      deviceId: optionsArg.deviceId,
      platform: optionsArg.platform,
      name: optionsArg.name,
      state: optionsArg.state,
      available: optionsArg.available,
      attributes: optionsArg.attributes,
    };
  }
}

function responseData(valueArg: unknown): Record<string, unknown> | undefined {
  const object = objectValue(valueArg);
  if (!object) {
    return undefined;
  }
  const body = objectValue(object.Body);
  const bodyData = objectValue(body?.Data) || objectValue(body?.LoggerInfo);
  if (bodyData) {
    return bodyData;
  }
  return object;
}

function collectionData(rawArg: unknown, kindArg: Exclude<TFroniusDeviceKind, 'system' | 'inverter'>): Record<string, unknown> {
  const data = responseData(rawArg);
  if (!data) {
    return {};
  }
  const collectionKey = kindArg === 'meter' ? 'meters' : kindArg === 'ohmpilot' ? 'ohmpilots' : 'storages';
  return objectValue(data[collectionKey]) || data;
}

function knownMappedValues(dataArg: Record<string, unknown>, mapArg: Record<string, string>): Record<string, unknown> {
  const result: Record<string, unknown> = {};
  for (const [rawKey, key] of Object.entries(mapArg)) {
    result[key] = solarApiValue(dataArg[rawKey]) ?? (dataArg[rawKey] === undefined ? undefined : withUnit(key, dataArg[rawKey])) ?? sensorValue(dataArg[key]);
  }
  for (const key of ['manufacturer', 'model', 'serial', 'enable', 'visible']) {
    result[key] = sensorValue(dataArg[key]) ?? objectValue(dataArg.Details)?.[pascalish(key)];
  }
  return cleanAttributes(result);
}

function flattenScalars(dataArg: Record<string, unknown>, prefixArg = ''): Record<string, unknown> {
  const result: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(dataArg)) {
    const normalizedKey = `${prefixArg}${prefixArg ? '_' : ''}${slug(key).replace(/-/g, '_')}`;
    const object = objectValue(value);
    if (object && ('Value' in object || 'value' in object)) {
      result[normalizedKey] = solarApiValue(object) ?? sensorValue(object);
      continue;
    }
    if (object && key !== 'Modules') {
      Object.assign(result, flattenScalars(object, normalizedKey));
      continue;
    }
    if (typeof value !== 'object') {
      result[normalizedKey] = withUnit(normalizedKey, value);
    }
  }
  return cleanAttributes(result);
}

function normalizeSensorValue(keyArg: string, valueArg: unknown): { value: unknown; unit?: string; sourceKey?: string } {
  const object = objectValue(valueArg);
  if (object) {
    const value = object.value ?? object.Value;
    const unit = stringValue(object.unit ?? object.Unit) || defaultUnit(keyArg);
    return { value, unit, sourceKey: stringValue(object.sourceKey) };
  }
  return { value: valueArg, unit: defaultUnit(keyArg) };
}

function solarApiValue(valueArg: unknown): { value: unknown; unit?: string } | undefined {
  const object = objectValue(valueArg);
  if (!object) {
    return undefined;
  }
  if ('Value' in object || 'Unit' in object) {
    return { value: object.Value, unit: stringValue(object.Unit) };
  }
  return undefined;
}

function sensorValue(valueArg: unknown): unknown {
  const object = objectValue(valueArg);
  if (object && 'value' in object) {
    return object.value;
  }
  if (object && 'Value' in object) {
    return object.Value;
  }
  return valueArg;
}

function withUnit(keyArg: string, valueArg: unknown): { value: unknown; unit?: string } {
  return { value: valueArg, unit: defaultUnit(keyArg) };
}

function defaultUnit(keyArg: string): string | undefined {
  if (keyArg.includes('energy')) {
    return 'Wh';
  }
  if (keyArg.includes('power')) {
    return keyArg.includes('reactive') ? 'VAr' : keyArg.includes('apparent') ? 'VA' : 'W';
  }
  if (keyArg.includes('current')) {
    return 'A';
  }
  if (keyArg.includes('voltage')) {
    return 'V';
  }
  if (keyArg.includes('frequency')) {
    return 'Hz';
  }
  if (keyArg.includes('temperature')) {
    return 'C';
  }
  if (keyArg.includes('state_of_charge') || keyArg.includes('relative_')) {
    return '%';
  }
  return undefined;
}

function sensorMeta(keyArg: string, unitArg: string | undefined): { deviceClass?: string; stateClass?: string; entityCategory?: string } {
  if (keyArg.includes('energy')) {
    return { deviceClass: 'energy', stateClass: 'total_increasing' };
  }
  if (keyArg.includes('power')) {
    return { deviceClass: unitArg === 'VA' ? 'apparent_power' : unitArg === 'VAr' ? 'reactive_power' : 'power', stateClass: 'measurement' };
  }
  if (keyArg.includes('current')) {
    return { deviceClass: 'current', stateClass: 'measurement' };
  }
  if (keyArg.includes('voltage')) {
    return { deviceClass: 'voltage', stateClass: 'measurement' };
  }
  if (keyArg.includes('frequency')) {
    return { deviceClass: 'frequency', stateClass: 'measurement' };
  }
  if (keyArg.includes('temperature')) {
    return { deviceClass: 'temperature', stateClass: 'measurement' };
  }
  if (keyArg === 'state_of_charge') {
    return { deviceClass: 'battery', stateClass: 'measurement' };
  }
  if (keyArg.includes('status') || keyArg.includes('error') || keyArg.includes('version') || keyArg.includes('identifier') || keyArg.includes('led')) {
    return { entityCategory: 'diagnostic' };
  }
  if (unitArg === '%') {
    return { stateClass: 'measurement' };
  }
  return {};
}

function diagnosticOptions(keyArg: string): { entityCategory?: string } {
  return sensorMeta(keyArg, undefined).entityCategory ? { entityCategory: 'diagnostic' } : {};
}

function parseEndpoint(valueArg: string | undefined, portArg?: number, protocolArg?: TFroniusProtocol): IEndpointInfo {
  if (!valueArg) {
    return { protocol: protocolArg, port: portArg };
  }
  const defaultProtocol = protocolArg || 'http';
  try {
    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' ? froniusDefaultHttpsPort : froniusDefaultHttpPort) };
  } catch {
    return { protocol: defaultProtocol, host: valueArg, port: portArg || (defaultProtocol === 'https' ? froniusDefaultHttpsPort : froniusDefaultHttpPort) };
  }
}

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

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

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 humanName(valueArg: string): string {
  return valueArg.replace(/_/g, ' ').replace(/\b\w/g, (matchArg) => matchArg.toUpperCase());
}

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

function pascalish(valueArg: string): string {
  return valueArg.split('_').map((partArg) => partArg.charAt(0).toUpperCase() + partArg.slice(1)).join('');
}

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

function deviceStateValue(valueArg: unknown): plugins.shxInterfaces.data.TDeviceStateValue {
  if (valueArg === undefined) {
    return null;
  }
  if (typeof valueArg === 'string' || typeof valueArg === 'number' || typeof valueArg === 'boolean' || valueArg === null) {
    return valueArg;
  }
  return { value: valueArg };
}
