import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type {
  IDaikinCommandRequest,
  IDaikinConfig,
  IDaikinDeviceSnapshotInfo,
  IDaikinEnergySnapshot,
  IDaikinRawData,
  IDaikinSnapshot,
  IDaikinValueMap,
  IDaikinZoneSnapshot,
  TDaikinApiVariant,
  TDaikinHvacAction,
  TDaikinHvacMode,
  TDaikinPresetMode,
  TDaikinSnapshotSource,
} from './daikin.types.js';
import { daikinDefaultHttpPort, daikinDisplayName, daikinDomain, daikinSkyFiPort } from './daikin.types.js';

interface IDaikinSnapshotOptions {
  config: IDaikinConfig;
  rawData?: Partial<IDaikinRawData>;
  online?: boolean;
  source?: TDaikinSnapshotSource;
  error?: string;
}

interface IDaikinEntityDefinition {
  key: string;
  name: string;
  value: unknown;
  platform?: TEntityPlatform;
  unit?: string;
  deviceClass?: string;
  stateClass?: string;
  entityCategory?: string;
  available?: boolean;
  attributes?: Record<string, unknown>;
}

const legacyTranslations: Record<string, Record<string, string>> = {
  mode: {
    '2': 'dry',
    '3': 'cool',
    '4': 'hot',
    '6': 'fan',
    '0': 'auto',
    '1': 'auto-1',
    '7': 'auto-7',
    '10': 'off',
  },
  f_rate: {
    A: 'auto',
    B: 'silence',
    '3': '1',
    '4': '2',
    '5': '3',
    '6': '4',
    '7': '5',
  },
  f_dir: {
    '0': 'off',
    '1': 'vertical',
    '2': 'horizontal',
    '3': '3d',
  },
  en_hol: {
    '0': 'off',
    '1': 'on',
  },
  en_streamer: {
    '0': 'off',
    '1': 'on',
  },
  adv: {
    '': 'off',
    '2': 'powerful',
    '2/13': 'powerful streamer',
    '12': 'econo',
    '12/13': 'econo streamer',
    '13': 'streamer',
  },
  spmode_kind: {
    '0': 'streamer',
    '1': 'powerful',
    '2': 'econo',
  },
  spmode: {
    '0': 'off',
    '1': 'on',
  },
};

const airbaseTranslations: Record<string, Record<string, string>> = {
  ...legacyTranslations,
  mode: {
    '0': 'fan',
    '1': 'hot',
    '2': 'cool',
    '3': 'auto',
    '7': 'dry',
  },
  f_rate: {
    '0': 'auto',
    '1': 'low',
    '3': 'mid',
    '5': 'high',
    '1a': 'low/auto',
    '3a': 'mid/auto',
    '5a': 'high/auto',
  },
};

const skyFiTranslations: Record<string, Record<string, string>> = {
  mode: {
    '0': 'off',
    '1': 'auto',
    '2': 'hot',
    '3': 'auto-3',
    '4': 'dry',
    '8': 'cool',
    '9': 'auto-9',
    '16': 'fan',
  },
  f_rate: {
    '1': 'low',
    '2': 'medium',
    '3': 'high',
    '5': 'low/auto',
    '6': 'medium/auto',
    '7': 'high/auto',
  },
};

const brp084Translations: Record<string, Record<string, string>> = {
  mode: {
    '0300': 'auto',
    '0200': 'cool',
    '0100': 'heat',
    '0000': 'fan',
    '0500': 'dry',
    '00': 'off',
    '01': 'on',
  },
  f_rate: {
    '0A00': 'auto',
    '0B00': 'quiet',
    '0300': '1',
    '0400': '2',
    '0500': '3',
    '0600': '4',
    '0700': '5',
  },
  f_dir: {
    off: 'off',
    vertical: 'vertical',
    horizontal: 'horizontal',
    both: '3d',
  },
  en_hol: {
    '0': 'off',
    '1': 'on',
  },
};

const rawDataKeys: Array<keyof IDaikinRawData> = [
  'basicInfo',
  'sensorInfo',
  'modelInfo',
  'controlInfo',
  'holiday',
  'notify',
  'dayPowerEx',
  'weekPower',
  'yearPower',
  'zoneSetting',
  'skyfiAc',
  'skyfiZones',
];

const skyFiToDaikin: Record<string, string> = {
  outsidetemp: 'otemp',
  roomtemp: 'htemp',
  settemp: 'stemp',
  opmode: 'pow',
  fanspeed: 'f_rate',
  acmode: 'mode',
};

const sensorDefinitions: Array<{ key: string; name: string; unit?: string; deviceClass?: string; stateClass?: string; value: (snapshotArg: IDaikinSnapshot) => unknown; enabled: (snapshotArg: IDaikinSnapshot) => boolean }> = [
  { key: 'inside_temperature', name: 'Inside Temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement', value: (snapshotArg) => snapshotArg.climate.insideTemperature, enabled: () => true },
  { key: 'outside_temperature', name: 'Outside Temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement', value: (snapshotArg) => snapshotArg.climate.outsideTemperature, enabled: (snapshotArg) => snapshotArg.capabilities.outsideTemperature },
  { key: 'humidity', name: 'Humidity', unit: '%', deviceClass: 'humidity', stateClass: 'measurement', value: (snapshotArg) => snapshotArg.climate.humidity, enabled: (snapshotArg) => snapshotArg.capabilities.humidity },
  { key: 'target_humidity', name: 'Target Humidity', unit: '%', deviceClass: 'humidity', stateClass: 'measurement', value: (snapshotArg) => snapshotArg.climate.targetHumidity, enabled: (snapshotArg) => snapshotArg.capabilities.humidity },
  { key: 'compressor_frequency', name: 'Compressor Frequency', unit: 'Hz', deviceClass: 'frequency', stateClass: 'measurement', value: (snapshotArg) => snapshotArg.climate.compressorFrequency, enabled: (snapshotArg) => snapshotArg.capabilities.compressorFrequency },
  { key: 'energy_today', name: 'Energy Consumption', unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing', value: (snapshotArg) => snapshotArg.energy.energyToday, enabled: (snapshotArg) => snapshotArg.capabilities.energyConsumption },
  { key: 'cool_energy', name: 'Cool Energy Consumption', unit: 'kWh', deviceClass: 'energy', value: (snapshotArg) => snapshotArg.energy.coolEnergyToday, enabled: (snapshotArg) => snapshotArg.capabilities.energyConsumption },
  { key: 'heat_energy', name: 'Heat Energy Consumption', unit: 'kWh', deviceClass: 'energy', value: (snapshotArg) => snapshotArg.energy.heatEnergyToday, enabled: (snapshotArg) => snapshotArg.capabilities.energyConsumption },
  { key: 'total_energy_today', name: 'Compressor Energy Consumption', unit: 'kWh', deviceClass: 'energy', stateClass: 'total_increasing', value: (snapshotArg) => snapshotArg.energy.totalEnergyToday, enabled: (snapshotArg) => snapshotArg.capabilities.energyConsumption },
  { key: 'total_power', name: 'Compressor Estimated Power Consumption', unit: 'kW', deviceClass: 'power', stateClass: 'measurement', value: (snapshotArg) => snapshotArg.energy.totalPower, enabled: (snapshotArg) => snapshotArg.energy.totalPower !== undefined },
];

export class DaikinMapper {
  public static toSnapshot(optionsArg: IDaikinSnapshotOptions): IDaikinSnapshot {
    if (optionsArg.config.snapshot && !optionsArg.rawData) {
      return this.normalizeSnapshot(this.clone(optionsArg.config.snapshot), optionsArg.config, optionsArg.source || 'snapshot');
    }

    const rawData = this.rawData(optionsArg.config, optionsArg.rawData);
    const values = this.valuesFromRawData(rawData);
    const variant = this.detectVariant(optionsArg.config, rawData, values);
    const device = this.deviceInfo(optionsArg.config, values, variant);
    const zones = this.zones(values, variant);
    const climate = this.climate(values, variant);
    const energy = this.energy(values);
    const capabilities = {
      localControl: Boolean(optionsArg.config.host || optionsArg.config.url || optionsArg.config.commandExecutor || optionsArg.config.client?.execute),
      fanRate: values.f_rate !== undefined,
      swingMode: values.f_dir !== undefined || values.f_dir_ud !== undefined || values.f_dir_lr !== undefined,
      awayMode: values.en_hol !== undefined,
      advancedModes: values.adv !== undefined,
      streamer: values.en_streamer !== undefined || (values.adv !== undefined && variant !== 'brp084'),
      outsideTemperature: climate.outsideTemperature !== undefined,
      humidity: climate.humidity !== undefined || climate.targetHumidity !== undefined,
      compressorFrequency: climate.compressorFrequency !== undefined,
      energyConsumption: Object.values(energy).some((valueArg) => valueArg !== undefined),
      zones: zones.length > 0,
      zoneTemperatureControl: Boolean(values.lztemp_c && values.lztemp_h),
    };

    return {
      device,
      climate,
      energy,
      zones,
      capabilities,
      rawData: Object.keys(values).length || rawData.brp084 ? { ...rawData, values } : undefined,
      online: optionsArg.online ?? optionsArg.config.online ?? Object.keys(values).length > 0,
      updatedAt: rawData.fetchedAt || new Date().toISOString(),
      source: optionsArg.source || (Object.keys(values).length ? 'manual' : 'runtime'),
      error: optionsArg.error,
    };
  }

  public static toDevices(snapshotArg: IDaikinSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'power', capability: 'switch', name: 'Power', readable: true, writable: snapshotArg.capabilities.localControl },
      { id: 'hvac_mode', capability: 'climate', name: 'HVAC mode', readable: true, writable: snapshotArg.capabilities.localControl },
      { id: 'target_temperature', capability: 'climate', name: 'Target temperature', readable: true, writable: snapshotArg.capabilities.localControl, unit: 'C' },
      { id: 'inside_temperature', capability: 'sensor', name: 'Inside temperature', readable: true, writable: false, unit: 'C' },
    ];
    if (snapshotArg.capabilities.outsideTemperature) {
      features.push({ id: 'outside_temperature', capability: 'sensor', name: 'Outside temperature', readable: true, writable: false, unit: 'C' });
    }
    if (snapshotArg.capabilities.humidity) {
      features.push({ id: 'humidity', capability: 'sensor', name: 'Humidity', readable: true, writable: false, unit: '%' });
    }
    if (snapshotArg.capabilities.fanRate) {
      features.push({ id: 'fan_mode', capability: 'fan', name: 'Fan mode', readable: true, writable: snapshotArg.capabilities.localControl });
    }
    if (snapshotArg.capabilities.swingMode) {
      features.push({ id: 'swing_mode', capability: 'fan', name: 'Swing mode', readable: true, writable: snapshotArg.capabilities.localControl });
    }
    if (snapshotArg.capabilities.energyConsumption) {
      features.push({ id: 'energy_today', capability: 'sensor', name: 'Energy today', readable: true, writable: false, unit: 'kWh' });
    }
    for (const zone of snapshotArg.zones) {
      features.push({ id: `zone_${zone.id}`, capability: 'switch', name: `${zone.name} zone`, readable: true, writable: snapshotArg.capabilities.localControl });
    }

    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'power', value: snapshotArg.climate.power, updatedAt },
      { featureId: 'hvac_mode', value: snapshotArg.climate.hvacMode, updatedAt },
      { featureId: 'target_temperature', value: snapshotArg.climate.targetTemperature ?? null, updatedAt },
      { featureId: 'inside_temperature', value: snapshotArg.climate.insideTemperature ?? null, updatedAt },
    ];
    if (snapshotArg.capabilities.outsideTemperature) {
      state.push({ featureId: 'outside_temperature', value: snapshotArg.climate.outsideTemperature ?? null, updatedAt });
    }
    if (snapshotArg.capabilities.humidity) {
      state.push({ featureId: 'humidity', value: snapshotArg.climate.humidity ?? null, updatedAt });
    }
    if (snapshotArg.capabilities.fanRate) {
      state.push({ featureId: 'fan_mode', value: snapshotArg.climate.fanMode ?? null, updatedAt });
    }
    if (snapshotArg.capabilities.swingMode) {
      state.push({ featureId: 'swing_mode', value: snapshotArg.climate.swingMode ?? null, updatedAt });
    }
    if (snapshotArg.capabilities.energyConsumption) {
      state.push({ featureId: 'energy_today', value: snapshotArg.energy.energyToday ?? null, updatedAt });
    }
    for (const zone of snapshotArg.zones) {
      state.push({ featureId: `zone_${zone.id}`, value: zone.enabled, updatedAt });
    }

    return [{
      id: this.deviceId(snapshotArg),
      integrationDomain: daikinDomain,
      name: snapshotArg.device.name,
      protocol: snapshotArg.device.host ? 'http' : 'unknown',
      manufacturer: snapshotArg.device.manufacturer,
      model: snapshotArg.device.model || 'Daikin AC',
      online: snapshotArg.online,
      features,
      state,
      metadata: this.cleanAttributes({
        host: snapshotArg.device.host,
        port: snapshotArg.device.port,
        ssl: snapshotArg.device.ssl,
        macAddress: snapshotArg.device.macAddress,
        firmware: snapshotArg.device.firmware,
        apiVariant: snapshotArg.device.apiVariant,
        source: snapshotArg.source,
        error: snapshotArg.error,
      }),
    }];
  }

  public static toEntities(snapshotArg: IDaikinSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    const deviceId = this.deviceId(snapshotArg);
    const uniqueBase = this.uniqueBase(snapshotArg);
    const baseName = snapshotArg.device.name;

    entities.push(this.entity(snapshotArg, {
      key: 'climate',
      name: baseName,
      platform: 'climate',
      value: snapshotArg.climate.hvacMode,
      attributes: {
        currentTemperature: snapshotArg.climate.insideTemperature,
        targetTemperature: snapshotArg.climate.targetTemperature,
        hvacAction: snapshotArg.climate.hvacAction,
        hvacModes: ['off', 'fan_only', 'dry', 'cool', 'heat', 'heat_cool'],
        fanMode: snapshotArg.climate.fanMode,
        fanModes: snapshotArg.climate.fanModes,
        swingMode: snapshotArg.climate.swingMode,
        swingModes: snapshotArg.climate.swingModes,
        presetMode: snapshotArg.climate.presetMode,
        presetModes: snapshotArg.climate.presetModes,
        temperatureUnit: 'C',
        supportedFeatures: this.supportedClimateFeatures(snapshotArg),
        rawDaikinMode: snapshotArg.climate.rawDaikinMode,
        apiVariant: snapshotArg.device.apiVariant,
        key: 'climate',
      },
    }, deviceId, uniqueBase));

    for (const definition of sensorDefinitions) {
      if (!definition.enabled(snapshotArg)) {
        continue;
      }
      this.pushEntity(entities, snapshotArg, deviceId, uniqueBase, {
        key: definition.key,
        name: `${baseName} ${definition.name}`,
        value: definition.value(snapshotArg),
        unit: definition.unit,
        deviceClass: definition.deviceClass,
        stateClass: definition.stateClass,
      });
    }

    entities.push(this.entity(snapshotArg, {
      key: 'power',
      name: `${baseName} Power`,
      platform: 'switch',
      value: snapshotArg.climate.power ? 'on' : 'off',
      attributes: { key: 'power' },
    }, deviceId, uniqueBase));

    if (snapshotArg.capabilities.streamer) {
      entities.push(this.entity(snapshotArg, {
        key: 'streamer',
        name: `${baseName} Streamer`,
        platform: 'switch',
        value: String(snapshotArg.climate.presetMode).includes('streamer') ? 'on' : this.advancedMode(snapshotArg.rawData?.values || {}).includes('streamer') ? 'on' : 'off',
        attributes: { key: 'streamer' },
      }, deviceId, uniqueBase));
    }

    for (const zone of snapshotArg.zones) {
      entities.push(this.entity(snapshotArg, {
        key: `zone_${zone.id}`,
        name: `${baseName} ${zone.name}`,
        platform: 'switch',
        value: zone.enabled ? 'on' : 'off',
        attributes: { key: 'zone', zoneId: zone.id },
      }, deviceId, uniqueBase));

      if (snapshotArg.capabilities.zoneTemperatureControl) {
        entities.push(this.entity(snapshotArg, {
          key: `zone_${zone.id}_temperature`,
          name: `${baseName} ${zone.name} Temperature`,
          platform: 'climate',
          value: snapshotArg.climate.hvacMode,
          attributes: {
            key: 'zone_temperature',
            zoneId: zone.id,
            targetTemperature: zone.targetTemperature,
            heatTargetTemperature: zone.heatTargetTemperature,
            coolTargetTemperature: zone.coolTargetTemperature,
            temperatureUnit: 'C',
          },
          available: zone.enabled && zone.targetTemperature !== undefined,
        }, deviceId, uniqueBase));
      }
    }

    return entities;
  }

  public static commandForService(snapshotArg: IDaikinSnapshot, requestArg: IServiceCallRequest): IDaikinCommandRequest | undefined {
    if (requestArg.domain === daikinDomain && requestArg.service === 'refresh') {
      return { action: 'refresh', service: requestArg.service, target: requestArg.target, data: requestArg.data };
    }

    if (requestArg.domain === 'climate') {
      if (requestArg.service === 'turn_on') {
        return this.controlCommand(snapshotArg, {}, requestArg);
      }
      if (requestArg.service === 'turn_off') {
        return this.controlCommand(snapshotArg, { mode: 'off' }, requestArg);
      }
      if (requestArg.service === 'set_temperature') {
        const temperature = this.numberValue(requestArg.data?.temperature);
        if (temperature === undefined) {
          return undefined;
        }
        const hvacMode = this.stringValue(requestArg.data?.hvac_mode ?? requestArg.data?.hvacMode);
        return this.controlCommand(snapshotArg, {
          stemp: this.formatTargetTemperature(temperature),
          mode: hvacMode ? this.hvacToDaikinMode(hvacMode) : undefined,
        }, requestArg);
      }
      if (requestArg.service === 'set_hvac_mode') {
        const hvacMode = this.stringValue(requestArg.data?.hvac_mode ?? requestArg.data?.hvacMode);
        return hvacMode ? this.controlCommand(snapshotArg, { mode: this.hvacToDaikinMode(hvacMode) }, requestArg) : undefined;
      }
      if (requestArg.service === 'set_fan_mode') {
        const fanMode = this.stringValue(requestArg.data?.fan_mode ?? requestArg.data?.fanMode);
        return fanMode ? this.controlCommand(snapshotArg, { f_rate: fanMode.toLowerCase() }, requestArg) : undefined;
      }
      if (requestArg.service === 'set_swing_mode') {
        const swingMode = this.stringValue(requestArg.data?.swing_mode ?? requestArg.data?.swingMode);
        return swingMode ? this.controlCommand(snapshotArg, { f_dir: swingMode.toLowerCase() }, requestArg) : undefined;
      }
      if (requestArg.service === 'set_preset_mode') {
        return this.presetCommand(snapshotArg, this.stringValue(requestArg.data?.preset_mode ?? requestArg.data?.presetMode), requestArg);
      }
    }

    if (requestArg.domain === 'switch') {
      const enabled = requestArg.service === 'turn_on' ? true : requestArg.service === 'turn_off' ? false : undefined;
      if (enabled === undefined) {
        return undefined;
      }
      const target = `${requestArg.target.entityId || ''} ${requestArg.target.deviceId || ''}`.toLowerCase();
      const zoneMatch = target.match(/(?:zone[_\-. ]?)(\d+)/);
      if (zoneMatch) {
        return { action: 'set_zone', protocol: snapshotArg.device.apiVariant, zoneId: Number(zoneMatch[1]), zoneKey: 'zone_onoff', zoneValue: enabled ? '1' : '0', service: requestArg.service, target: requestArg.target, data: requestArg.data };
      }
      if (target.includes('streamer')) {
        return { action: 'set_streamer', protocol: snapshotArg.device.apiVariant, enabled, mode: enabled ? 'on' : 'off', service: requestArg.service, target: requestArg.target, data: requestArg.data };
      }
      return this.controlCommand(snapshotArg, { mode: enabled ? undefined : 'off' }, requestArg);
    }

    if (requestArg.domain === daikinDomain && requestArg.service === 'set_zone') {
      const zoneId = this.numberValue(requestArg.data?.zone_id ?? requestArg.data?.zoneId);
      const value = requestArg.data?.value ?? requestArg.data?.enabled ?? requestArg.data?.on;
      if (zoneId === undefined) {
        return undefined;
      }
      return {
        action: 'set_zone',
        protocol: snapshotArg.device.apiVariant,
        zoneId,
        zoneKey: this.stringValue(requestArg.data?.key) || 'zone_onoff',
        zoneValue: typeof value === 'boolean' ? value ? '1' : '0' : String(value ?? '1'),
        service: requestArg.service,
        target: requestArg.target,
        data: requestArg.data,
      };
    }

    return undefined;
  }

  public static deviceId(snapshotArg: IDaikinSnapshot): string {
    return `${daikinDomain}.ac.${this.uniqueBase(snapshotArg)}`;
  }

  public static snapshotId(snapshotArg: IDaikinSnapshot): string | undefined {
    return snapshotArg.device.macAddress || snapshotArg.device.id || snapshotArg.device.host;
  }

  public static uniqueBase(snapshotArg: IDaikinSnapshot): string {
    return this.slug(snapshotArg.device.macAddress || snapshotArg.device.id || snapshotArg.device.host || snapshotArg.device.name || daikinDomain);
  }

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

  public static normalizeMac(valueArg?: string): string | undefined {
    if (!valueArg) {
      return undefined;
    }
    const compact = valueArg.replace(/[^a-fA-F0-9]/g, '').toLowerCase();
    return compact.length === 12 ? compact.match(/.{1,2}/g)?.join(':') : valueArg.toLowerCase();
  }

  public static compactMac(valueArg?: string): string | undefined {
    const normalized = this.normalizeMac(valueArg);
    return normalized?.replace(/[^a-fA-F0-9]/g, '').toLowerCase();
  }

  public static daikinToHuman(keyArg: string, valueArg: string | undefined, variantArg: TDaikinApiVariant = 'brp069'): string | undefined {
    if (valueArg === undefined) {
      return undefined;
    }
    return this.translations(variantArg)[keyArg]?.[valueArg] || valueArg;
  }

  public static humanToDaikin(keyArg: string, valueArg: string | number | undefined, variantArg: TDaikinApiVariant = 'brp069'): string | undefined {
    if (valueArg === undefined) {
      return undefined;
    }
    const normalized = String(valueArg).trim().toLowerCase().replace(/^heat$/, 'hot').replace(/^fan_only$/, 'fan').replace(/^heat_cool$/, 'auto');
    const translation = this.translations(variantArg)[keyArg] || {};
    for (const [raw, human] of Object.entries(translation)) {
      if (human.toLowerCase() === normalized || raw.toLowerCase() === normalized) {
        return raw;
      }
    }
    return normalized;
  }

  public static hvacToDaikinMode(valueArg: string): string {
    const normalized = valueArg.toLowerCase();
    if (normalized === 'fan_only') {
      return 'fan';
    }
    if (normalized === 'heat') {
      return 'hot';
    }
    if (normalized === 'heat_cool') {
      return 'auto';
    }
    return normalized;
  }

  public static formatTargetTemperature(valueArg: number): string {
    return String(Math.round(Number(valueArg) * 2) / 2).replace(/\.0$/, '');
  }

  public static parseKeyValueResponse(responseBodyArg: string): IDaikinValueMap {
    const response: IDaikinValueMap = {};
    const pattern = /(\w+)=([^=]*)(?:,|$)/g;
    let match: RegExpExecArray | null;
    while ((match = pattern.exec(responseBodyArg))) {
      response[match[1]] = match[2];
    }
    if (!('ret' in response)) {
      throw new Error("Daikin response is missing 'ret'.");
    }
    if (response.ret !== 'OK') {
      return {};
    }
    delete response.ret;
    if (response.name) {
      response.name = this.safeDecode(response.name);
    }
    this.normalizeCompositeSwing(response);
    return response;
  }

  public static parseSkyFiResponse(responseBodyArg: string): IDaikinValueMap {
    const response: IDaikinValueMap = {};
    for (const item of responseBodyArg.split('&')) {
      const index = item.indexOf('=');
      if (index === -1) {
        continue;
      }
      response[item.slice(0, index)] = item.slice(index + 1);
    }
    if (response.fanflags === '3' && response.fanspeed && /^\d+$/.test(response.fanspeed)) {
      response.fanspeed = String(Number(response.fanspeed) + 4);
    }
    const mapped: IDaikinValueMap = { ...response };
    for (const [skyfiKey, daikinKey] of Object.entries(skyFiToDaikin)) {
      if (response[skyfiKey] !== undefined) {
        mapped[daikinKey] = response[skyfiKey];
      }
    }
    return mapped;
  }

  public static valuesFromRawData(rawDataArg: Partial<IDaikinRawData> = {}): IDaikinValueMap {
    const values: IDaikinValueMap = {};
    for (const key of rawDataKeys) {
      const data = rawDataArg[key];
      if (data && typeof data === 'object' && !Array.isArray(data)) {
        Object.assign(values, data);
      }
    }
    for (const data of Object.values(rawDataArg.resources || {})) {
      Object.assign(values, data);
    }
    Object.assign(values, rawDataArg.values || {});
    if (rawDataArg.skyfiAc) {
      Object.assign(values, this.parseSkyFiMap(rawDataArg.skyfiAc));
    }
    this.normalizeCompositeSwing(values);
    return values;
  }

  private static normalizeSnapshot(snapshotArg: IDaikinSnapshot, configArg: IDaikinConfig, sourceArg: TDaikinSnapshotSource): IDaikinSnapshot {
    const rawData = this.rawData(configArg, snapshotArg.rawData);
    const derived = this.toSnapshot({ config: { ...configArg, snapshot: undefined }, rawData, online: snapshotArg.online, source: sourceArg, error: snapshotArg.error });
    return {
      ...derived,
      ...snapshotArg,
      device: { ...derived.device, ...snapshotArg.device },
      climate: { ...derived.climate, ...snapshotArg.climate },
      energy: { ...derived.energy, ...snapshotArg.energy },
      capabilities: { ...derived.capabilities, ...snapshotArg.capabilities },
      zones: snapshotArg.zones?.length ? snapshotArg.zones : derived.zones,
      rawData: Object.keys(rawData).length ? rawData : snapshotArg.rawData,
      source: snapshotArg.source || sourceArg,
      updatedAt: snapshotArg.updatedAt || new Date().toISOString(),
    };
  }

  private static rawData(configArg: IDaikinConfig, rawDataArg?: Partial<IDaikinRawData>): Partial<IDaikinRawData> {
    return this.cleanAttributes({
      ...configArg.rawData,
      ...rawDataArg,
      values: this.cleanAttributes({ ...configArg.rawData?.values, ...configArg.values, ...rawDataArg?.values }),
      resources: this.cleanAttributes({ ...configArg.rawData?.resources, ...rawDataArg?.resources }),
    }) as Partial<IDaikinRawData>;
  }

  private static deviceInfo(configArg: IDaikinConfig, valuesArg: IDaikinValueMap, variantArg: TDaikinApiVariant): IDaikinDeviceSnapshotInfo {
    const parsed = this.parseEndpoint(configArg.url || configArg.host);
    const host = parsed?.host || configArg.host;
    const ssl = configArg.ssl ?? configArg.tls ?? parsed?.ssl ?? variantArg === 'brp072c';
    const port = configArg.port || parsed?.port || (variantArg === 'skyfi' ? daikinSkyFiPort : ssl ? 443 : daikinDefaultHttpPort);
    const macAddress = this.normalizeMac(valuesArg.mac || configArg.macAddress);
    const name = configArg.name || this.safeDecode(valuesArg.name) || host || daikinDisplayName;
    const model = configArg.model || (valuesArg.model === 'NOTSUPPORT' && variantArg === 'airbase' ? 'Airbase BRP15B61' : valuesArg.model);
    return {
      id: configArg.uniqueId || macAddress || (host ? `${host}:${port}` : undefined) || name,
      name,
      manufacturer: configArg.manufacturer || 'Daikin',
      model,
      firmware: valuesArg.ver?.replace(/_/g, '.'),
      adapterVersion: valuesArg.rev || valuesArg.adp_kind,
      host,
      port,
      ssl,
      macAddress,
      apiVariant: variantArg,
    };
  }

  private static climate(valuesArg: IDaikinValueMap, variantArg: TDaikinApiVariant): IDaikinSnapshot['climate'] {
    const rawPower = valuesArg.pow;
    const rawMode = this.rawMode(valuesArg, variantArg);
    const daikinMode = rawPower === '0' ? 'off' : this.daikinToHuman('mode', rawMode, variantArg) || rawMode || 'unknown';
    const hvacMode = this.daikinModeToHvac(daikinMode);
    const compressorFrequency = this.numberValue(valuesArg.cmpfreq);
    const presetMode = this.presetMode(valuesArg, variantArg);
    return {
      power: hvacMode !== 'off' && rawPower !== '0',
      hvacMode,
      hvacAction: this.hvacAction(hvacMode, compressorFrequency),
      rawDaikinMode: daikinMode,
      rawPower,
      targetTemperature: this.numberValue(valuesArg.stemp),
      insideTemperature: this.numberValue(valuesArg.htemp),
      outsideTemperature: this.numberValue(valuesArg.otemp),
      humidity: this.numberValue(valuesArg.hhum),
      targetHumidity: this.numberValue(valuesArg.shum),
      compressorFrequency,
      fanMode: this.title(this.daikinToHuman('f_rate', valuesArg.f_rate, variantArg)),
      swingMode: this.title(this.daikinToHuman('f_dir', valuesArg.f_dir, variantArg)),
      presetMode,
      fanModes: this.modeList('f_rate', variantArg),
      swingModes: this.modeList('f_dir', variantArg),
      presetModes: this.presetModes(valuesArg),
      temperatureUnit: 'C',
      targetTemperatureStep: 1,
    };
  }

  private static rawMode(valuesArg: IDaikinValueMap, variantArg: TDaikinApiVariant): string | undefined {
    if (variantArg === 'skyfi' && valuesArg.acmode !== undefined) {
      return valuesArg.acmode;
    }
    return valuesArg.mode;
  }

  private static energy(valuesArg: IDaikinValueMap): IDaikinEnergySnapshot {
    const coolEnergyToday = this.sumSlashNumbers(valuesArg.curr_day_cool, 10);
    const heatEnergyToday = this.sumSlashNumbers(valuesArg.curr_day_heat, 10);
    const totalEnergyToday = this.lastSlashNumber(valuesArg.datas, 1000);
    const directTotalPower = this.numberValue(valuesArg.total_power || valuesArg.current_total_power);
    return this.cleanAttributes({
      energyToday: coolEnergyToday !== undefined || heatEnergyToday !== undefined ? (coolEnergyToday || 0) + (heatEnergyToday || 0) : undefined,
      coolEnergyToday,
      heatEnergyToday,
      totalEnergyToday,
      totalPower: directTotalPower,
      lastHourCoolEnergy: this.lastSlashNumber(valuesArg.cool_energy, 10),
      lastHourHeatEnergy: this.lastSlashNumber(valuesArg.heat_energy, 10),
    }) as IDaikinEnergySnapshot;
  }

  private static zones(valuesArg: IDaikinValueMap, variantArg: TDaikinApiVariant): IDaikinZoneSnapshot[] {
    if (variantArg === 'skyfi' && valuesArg.nz) {
      const count = Math.max(0, Math.trunc(this.numberValue(valuesArg.nz) || 0));
      const zoneBits = this.skyFiZoneBits(valuesArg.zone, count);
      return Array.from({ length: count }, (_, indexArg) => ({
        id: indexArg,
        name: this.safeDecode(valuesArg[`zone${indexArg + 1}`])?.trim().replace(/[ +,]+$/g, '') || `Zone ${indexArg + 1}`,
        enabled: zoneBits[indexArg] === '1',
      })).filter((zoneArg) => zoneArg.name !== `Zone ${zoneArg.id + 1}` || zoneBits[zoneArg.id] !== undefined);
    }

    const names = this.listValue(valuesArg.zone_name);
    if (!names.length) {
      return [];
    }
    const enabledCount = this.numberValue(valuesArg.en_zone);
    const limit = enabledCount === undefined ? names.length : Math.max(0, Math.trunc(enabledCount));
    const zoneOnOff = this.listValue(valuesArg.zone_onoff);
    const heatTemps = this.listValue(valuesArg.lztemp_h);
    const coolTemps = this.listValue(valuesArg.lztemp_c);
    const mode = this.daikinModeToHvac(this.daikinToHuman('mode', valuesArg.mode, variantArg) || valuesArg.mode || 'unknown');
    return names.slice(0, limit).map((nameArg, indexArg) => {
      const heatTargetTemperature = this.numberValue(heatTemps[indexArg]);
      const coolTargetTemperature = this.numberValue(coolTemps[indexArg]);
      return {
        id: indexArg,
        name: nameArg.trim().replace(/[ +,]+$/g, '') || `Zone ${indexArg + 1}`,
        enabled: zoneOnOff[indexArg] === '1',
        targetTemperature: mode === 'heat' ? heatTargetTemperature : mode === 'cool' ? coolTargetTemperature : this.numberValue(valuesArg.stemp),
        heatTargetTemperature,
        coolTargetTemperature,
      };
    }).filter((zoneArg) => zoneArg.name !== '-');
  }

  private static pushEntity(entitiesArg: IIntegrationEntity[], snapshotArg: IDaikinSnapshot, deviceIdArg: string, uniqueBaseArg: string, definitionArg: IDaikinEntityDefinition): void {
    if (definitionArg.value === undefined || definitionArg.value === null) {
      return;
    }
    entitiesArg.push(this.entity(snapshotArg, definitionArg, deviceIdArg, uniqueBaseArg));
  }

  private static entity(snapshotArg: IDaikinSnapshot, definitionArg: IDaikinEntityDefinition, deviceIdArg: string, uniqueBaseArg: string): IIntegrationEntity {
    const platform = definitionArg.platform || 'sensor';
    return {
      id: `${platform}.${this.slug(definitionArg.name)}`,
      uniqueId: `${daikinDomain}_${uniqueBaseArg}_${this.slug(definitionArg.key)}`,
      integrationDomain: daikinDomain,
      deviceId: deviceIdArg,
      platform,
      name: definitionArg.name,
      state: definitionArg.value,
      attributes: this.cleanAttributes({
        key: definitionArg.key,
        unit: definitionArg.unit,
        deviceClass: definitionArg.deviceClass,
        stateClass: definitionArg.stateClass,
        entityCategory: definitionArg.entityCategory,
        source: snapshotArg.source,
        error: snapshotArg.error,
        ...definitionArg.attributes,
      }),
      available: definitionArg.available ?? snapshotArg.online,
    };
  }

  private static controlCommand(snapshotArg: IDaikinSnapshot, settingsArg: IDaikinCommandRequest['settings'], requestArg: IServiceCallRequest): IDaikinCommandRequest {
    return { action: 'set_control', protocol: snapshotArg.device.apiVariant, settings: this.cleanAttributes(settingsArg || {}) as IDaikinCommandRequest['settings'], service: requestArg.service, target: requestArg.target, data: requestArg.data };
  }

  private static presetCommand(snapshotArg: IDaikinSnapshot, presetArg: string | undefined, requestArg: IServiceCallRequest): IDaikinCommandRequest | undefined {
    if (!presetArg) {
      return undefined;
    }
    const preset = presetArg.toLowerCase();
    if (preset === 'away') {
      return { action: 'set_holiday', protocol: snapshotArg.device.apiVariant, enabled: true, mode: 'on', service: requestArg.service, target: requestArg.target, data: requestArg.data };
    }
    if (preset === 'boost') {
      return { action: 'set_advanced_mode', protocol: snapshotArg.device.apiVariant, enabled: true, mode: 'powerful', service: requestArg.service, target: requestArg.target, data: requestArg.data };
    }
    if (preset === 'eco') {
      return { action: 'set_advanced_mode', protocol: snapshotArg.device.apiVariant, enabled: true, mode: 'econo', service: requestArg.service, target: requestArg.target, data: requestArg.data };
    }
    if (preset === 'none') {
      if (snapshotArg.climate.presetMode === 'away') {
        return { action: 'set_holiday', protocol: snapshotArg.device.apiVariant, enabled: false, mode: 'off', service: requestArg.service, target: requestArg.target, data: requestArg.data };
      }
      if (snapshotArg.climate.presetMode === 'boost') {
        return { action: 'set_advanced_mode', protocol: snapshotArg.device.apiVariant, enabled: false, mode: 'powerful', service: requestArg.service, target: requestArg.target, data: requestArg.data };
      }
      if (snapshotArg.climate.presetMode === 'eco') {
        return { action: 'set_advanced_mode', protocol: snapshotArg.device.apiVariant, enabled: false, mode: 'econo', service: requestArg.service, target: requestArg.target, data: requestArg.data };
      }
    }
    return undefined;
  }

  private static supportedClimateFeatures(snapshotArg: IDaikinSnapshot): string[] {
    const features = ['turn_on', 'turn_off', 'target_temperature'];
    if (snapshotArg.capabilities.fanRate) {
      features.push('fan_mode');
    }
    if (snapshotArg.capabilities.swingMode) {
      features.push('swing_mode');
    }
    if (snapshotArg.capabilities.awayMode || snapshotArg.capabilities.advancedModes) {
      features.push('preset_mode');
    }
    return features;
  }

  private static detectVariant(configArg: IDaikinConfig, rawDataArg: Partial<IDaikinRawData>, valuesArg: IDaikinValueMap): TDaikinApiVariant {
    if (configArg.apiVariant && configArg.apiVariant !== 'auto') {
      return configArg.apiVariant;
    }
    if (rawDataArg.variant && rawDataArg.variant !== 'auto') {
      return rawDataArg.variant;
    }
    if (rawDataArg.brp084) {
      return 'brp084';
    }
    if (rawDataArg.skyfiAc || valuesArg.acmode !== undefined || valuesArg.opmode !== undefined) {
      return 'skyfi';
    }
    if (Object.keys(rawDataArg.resources || {}).some((keyArg) => keyArg.startsWith('skyfi/')) || valuesArg.lztemp_c !== undefined || valuesArg.zone_name !== undefined) {
      return 'airbase';
    }
    if (configArg.apiKey) {
      return 'brp072c';
    }
    return 'brp069';
  }

  private static daikinModeToHvac(valueArg?: string): TDaikinHvacMode {
    const value = (valueArg || '').toLowerCase();
    if (value === 'fan') {
      return 'fan_only';
    }
    if (value === 'hot' || value === 'heat') {
      return 'heat';
    }
    if (value === 'auto' || value.startsWith('auto-')) {
      return 'heat_cool';
    }
    if (value === 'cool' || value === 'dry' || value === 'off') {
      return value;
    }
    return value || 'unknown';
  }

  private static hvacAction(hvacModeArg: TDaikinHvacMode, compressorFrequencyArg?: number): TDaikinHvacAction {
    if (hvacModeArg === 'off') {
      return 'off';
    }
    if (compressorFrequencyArg === 0 && (hvacModeArg === 'cool' || hvacModeArg === 'heat')) {
      return 'idle';
    }
    if (hvacModeArg === 'cool') {
      return 'cooling';
    }
    if (hvacModeArg === 'heat') {
      return 'heating';
    }
    if (hvacModeArg === 'dry') {
      return 'drying';
    }
    if (hvacModeArg === 'fan_only') {
      return 'fan';
    }
    return 'idle';
  }

  private static presetMode(valuesArg: IDaikinValueMap, variantArg: TDaikinApiVariant): TDaikinPresetMode {
    if (this.daikinToHuman('en_hol', valuesArg.en_hol, variantArg) === 'on') {
      return 'away';
    }
    const advancedMode = this.advancedMode(valuesArg, variantArg);
    if (advancedMode.includes('powerful')) {
      return 'boost';
    }
    if (advancedMode.includes('econo')) {
      return 'eco';
    }
    return 'none';
  }

  private static presetModes(valuesArg: IDaikinValueMap): TDaikinPresetMode[] {
    const modes: TDaikinPresetMode[] = ['none'];
    if (valuesArg.en_hol !== undefined) {
      modes.push('away');
    }
    if (valuesArg.adv !== undefined) {
      modes.push('eco', 'boost');
    }
    return modes;
  }

  private static advancedMode(valuesArg: IDaikinValueMap, variantArg: TDaikinApiVariant = 'brp069'): string {
    return this.daikinToHuman('adv', valuesArg.adv, variantArg)?.toLowerCase() || '';
  }

  private static modeList(keyArg: string, variantArg: TDaikinApiVariant): string[] {
    return Object.values(this.translations(variantArg)[keyArg] || {}).map((valueArg) => this.title(valueArg) || valueArg).filter((valueArg, indexArg, arrayArg) => arrayArg.indexOf(valueArg) === indexArg);
  }

  private static translations(variantArg: TDaikinApiVariant): Record<string, Record<string, string>> {
    if (variantArg === 'airbase') {
      return airbaseTranslations;
    }
    if (variantArg === 'skyfi') {
      return skyFiTranslations;
    }
    if (variantArg === 'brp084') {
      return brp084Translations;
    }
    return legacyTranslations;
  }

  private static parseSkyFiMap(valueArg: IDaikinValueMap): IDaikinValueMap {
    const mapped: IDaikinValueMap = { ...valueArg };
    for (const [skyfiKey, daikinKey] of Object.entries(skyFiToDaikin)) {
      if (valueArg[skyfiKey] !== undefined) {
        mapped[daikinKey] = valueArg[skyfiKey];
      }
    }
    return mapped;
  }

  private static normalizeCompositeSwing(valuesArg: IDaikinValueMap): void {
    if (valuesArg.f_dir !== undefined || valuesArg.f_dir_ud === undefined || valuesArg.f_dir_lr === undefined) {
      return;
    }
    if (valuesArg.f_dir_ud === '0' && valuesArg.f_dir_lr === '0') {
      valuesArg.f_dir = '0';
    } else if (valuesArg.f_dir_ud === 'S' && valuesArg.f_dir_lr === '0') {
      valuesArg.f_dir = '1';
    } else if (valuesArg.f_dir_ud === '0' && valuesArg.f_dir_lr === 'S') {
      valuesArg.f_dir = '2';
    } else if (valuesArg.f_dir_ud === 'S' && valuesArg.f_dir_lr === 'S') {
      valuesArg.f_dir = '3';
    }
  }

  private static listValue(valueArg?: string): string[] {
    if (!valueArg) {
      return [];
    }
    const decoded = this.safeDecode(valueArg) || valueArg;
    if (decoded.includes(';')) {
      return decoded.split(';');
    }
    return decoded.split('');
  }

  private static skyFiZoneBits(valueArg: string | undefined, countArg: number): string[] {
    if (!valueArg) {
      return [];
    }
    if (/^[01]+$/.test(valueArg)) {
      return valueArg.split('').slice(0, countArg);
    }
    const parsed = Number(valueArg);
    if (!Number.isFinite(parsed)) {
      return [];
    }
    return (parsed + 256).toString(2).slice(1, countArg + 1).split('');
  }

  private static sumSlashNumbers(valueArg: string | undefined, dividerArg: number): number | undefined {
    const values = this.slashNumbers(valueArg);
    if (!values.length) {
      return undefined;
    }
    return values.reduce((sumArg, value) => sumArg + value, 0) / dividerArg;
  }

  private static lastSlashNumber(valueArg: string | undefined, dividerArg: number): number | undefined {
    const values = this.slashNumbers(valueArg);
    if (!values.length) {
      return undefined;
    }
    return values[values.length - 1] / dividerArg;
  }

  private static slashNumbers(valueArg?: string): number[] {
    if (!valueArg) {
      return [];
    }
    return valueArg.split('/').map((itemArg) => Number(itemArg)).filter((itemArg) => Number.isFinite(itemArg));
  }

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

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

  private static title(valueArg: string | undefined): string | undefined {
    if (!valueArg) {
      return undefined;
    }
    return valueArg.split('/').map((partArg) => partArg ? `${partArg[0].toUpperCase()}${partArg.slice(1)}` : partArg).join('/');
  }

  private static parseEndpoint(valueArg: string | undefined): { host: string; port?: number; ssl: boolean } | undefined {
    if (!valueArg || !/^[a-z][a-z0-9+.-]*:\/\//i.test(valueArg)) {
      return undefined;
    }
    try {
      const url = new URL(valueArg);
      return { host: url.hostname, port: url.port ? Number(url.port) : undefined, ssl: url.protocol === 'https:' };
    } catch {
      return undefined;
    }
  }

  private static safeDecode(valueArg: string | undefined): string | undefined {
    if (valueArg === undefined) {
      return undefined;
    }
    try {
      return decodeURIComponent(valueArg.replace(/\+/g, '%20'));
    } catch {
      return valueArg;
    }
  }

  private static cleanAttributes<T extends Record<string, unknown>>(recordArg: T): Partial<T> {
    const cleaned: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(recordArg)) {
      if (value !== undefined) {
        cleaned[key] = value;
      }
    }
    return cleaned as Partial<T>;
  }

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