import { SimpleLocalClient, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot, type IServiceCallRequest, type IServiceCallResult } from '../../core/index.js';
import { LektricoMapper } from './lektrico.mapper.js';
import type { ILektricoConfig, ILektricoRawData, ILektricoSettings, TLektricoDeviceType } from './lektrico.types.js';
import { lektricoDefaultPort, lektricoDeviceTypes, lektricoProfile } from './lektrico.types.js';

export class LektricoClientError extends Error {
  constructor(messageArg: string) {
    super(messageArg);
    this.name = 'LektricoClientError';
  }
}

interface ILektricoEndpoint {
  protocol: 'http';
  host: string;
  port?: number;
}

interface ILektricoCommand {
  key: string;
  method: string;
  params?: Record<string, unknown>;
}

export class LektricoClient extends SimpleLocalClient<ILektricoConfig> {
  private liveSnapshot?: ISimpleLocalSnapshot;

  constructor(private readonly configArg: ILektricoConfig) {
    super(lektricoProfile, configArg);
  }

  public override async getSnapshot(forceRefreshArg = false): Promise<ISimpleLocalSnapshot> {
    if (!forceRefreshArg && this.liveSnapshot) {
      return clone(this.liveSnapshot);
    }

    if (!this.hasLiveHttpConfig()) {
      return super.getSnapshot(forceRefreshArg);
    }

    try {
      this.liveSnapshot = await this.fetchSnapshot();
      return clone(this.liveSnapshot);
    } catch (errorArg) {
      this.liveSnapshot = LektricoMapper.toSnapshot({
        config: this.configArg,
        online: false,
        source: 'http',
        error: errorMessage(errorArg),
      });
      return clone(this.liveSnapshot);
    }
  }

  public override async refresh(): Promise<IServiceCallResult> {
    this.liveSnapshot = undefined;
    if (!this.hasLiveHttpConfig()) {
      return super.refresh();
    }

    const snapshot = await this.getSnapshot(true);
    const success = snapshot.online && !snapshot.error;
    return { success, error: success ? undefined : snapshot.error, data: snapshot };
  }

  public override async execute(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (this.configArg.client?.execute || this.configArg.commandExecutor || !this.hasLiveHttpConfig()) {
      return super.execute(requestArg);
    }

    try {
      const command = await this.commandForRequest(requestArg);
      const response = await this.rpcPost(command);
      if (response.result === false || response.error) {
        return { success: false, error: `Lektrico command ${command.method} failed: ${JSON.stringify(response).slice(0, 200)}`, data: response };
      }
      this.liveSnapshot = undefined;
      return { success: true, data: { command, response } };
    } catch (errorArg) {
      return { success: false, error: errorMessage(errorArg) };
    }
  }

  public async getRawData(): Promise<ILektricoRawData> {
    const settings = await this.deviceConfig();
    const info = await this.deviceInfo(settings.type);
    return this.rawDataFrom(settings, info);
  }

  private async fetchSnapshot(): Promise<ISimpleLocalSnapshot> {
    const rawData = await this.getRawData();
    return LektricoMapper.toSnapshot({ config: this.configArg, rawData, online: true, source: 'http' });
  }

  private async deviceConfig(): Promise<ILektricoSettings> {
    const deviceIdResponse = await this.rpcGet('Device_id.Get');
    const deviceId = stringValue(deviceIdResponse.device_id);
    if (!deviceId) {
      throw new LektricoClientError('Lektrico Device_id.Get did not return device_id.');
    }

    const type = deviceTypeFromId(deviceId);
    const settingsResponse = await this.rpcGet(type === lektricoDeviceTypes.energyMeter1p || type === lektricoDeviceTypes.energyMeter3p ? 'M2w_config.Get' : 'charger_config.get');
    const serialNumber = settingsResponse.serial_number ?? this.configArg.serialNumber;
    if (serialNumber === undefined || serialNumber === null || serialNumber === '') {
      throw new LektricoClientError('Lektrico configuration did not return serial_number.');
    }

    return {
      type,
      serial_number: String(serialNumber),
      board_revision: stringValue(settingsResponse.board_revision) || this.configArg.boardRevision,
    };
  }

  private async deviceInfo(deviceTypeArg: TLektricoDeviceType): Promise<Record<string, unknown>> {
    if (deviceTypeArg === lektricoDeviceTypes.energyMeter1p || deviceTypeArg === lektricoDeviceTypes.energyMeter3p) {
      const meterInfo = await this.rpcGet('Meter_info.Get');
      const appConfig = await this.rpcGet('App_config.Get');
      const version = await this.rpcGet('Sw_version.Get');
      const data = { ...meterInfo, ...appConfig, ...version };
      const current = numberArray(data.current);
      const voltage = numberArray(data.voltage);
      const activePower = numberArray(data.active_p);
      const powerFactor = numberArray(data.power_factor);
      return {
        ...data,
        lb_mode: numberValue(data.load_balancing_mode) ?? 0,
        breaker_curent: numberValue(data.breaker_rating) ?? 0,
        current_l1: current[0] ?? 0,
        current_l2: current[1] ?? 0,
        current_l3: current[2] ?? 0,
        voltage_l1: voltage[0] ?? 0,
        voltage_l2: voltage[1] ?? 0,
        voltage_l3: voltage[2] ?? 0,
        power_l1: activePower[0] ?? 0,
        power_l2: activePower[1] ?? 0,
        power_l3: activePower[2] ?? 0,
        power_factor_l1: powerFactor[0] ?? 0,
        power_factor_l2: powerFactor[1] ?? 0,
        power_factor_l3: powerFactor[2] ?? 0,
      };
    }

    const chargerInfo = await this.rpcGet('charger_info.get');
    const appConfig = await this.rpcGet('app_config.get');
    const data: Record<string, unknown> = { ...chargerInfo, ...appConfig };
    if (data.has_active_errors === true) {
      Object.assign(data, await this.rpcGet('active_errors.get'));
    } else {
      Object.assign(data, activeErrorDefaults);
    }

    delete data.dynamic_current;
    Object.assign(data, await this.rpcGet('dynamic_current.get'));

    const currents = numberArray(data.currents);
    const voltages = numberArray(data.voltages);
    return {
      ...data,
      charger_state: readableChargerState(stringValue(data.extended_charger_state) || stringValue(data.charger_state) || ''),
      current_limit_reason: currentLimitReason(data.current_limit_reason),
      state_e_activated: Boolean(data.state_e_activated ?? data.state_machine_e_activated),
      relay_mode: numberValue(data.relay_mode) ?? -1,
      current_l1: currents[0] ?? 0,
      current_l2: currents[1] ?? 0,
      current_l3: currents[2] ?? 0,
      voltage_l1: voltages[0] ?? 0,
      voltage_l2: voltages[1] ?? 0,
      voltage_l3: voltages[2] ?? 0,
      require_auth: !Boolean(data.headless),
    };
  }

  private rawDataFrom(settingsArg: ILektricoSettings, infoArg: Record<string, unknown>): ILektricoRawData {
    const endpoint = this.endpoint();
    const port = endpoint?.port || this.configArg.port || lektricoDefaultPort;
    const serialNumber = String(settingsArg.serial_number);
    const deviceName = this.configArg.name || `${settingsArg.type}_${serialNumber}`;
    return {
      device: {
        id: this.configArg.uniqueId || this.configArg.deviceId || serialNumber,
        name: deviceName,
        manufacturer: lektricoProfile.manufacturer,
        model: settingsArg.type.toUpperCase(),
        serialNumber,
        host: endpoint?.host || this.configArg.host,
        port,
        protocol: 'http',
        configurationUrl: endpoint ? `http://${endpoint.host}${port === 80 ? '' : `:${port}`}` : undefined,
        attributes: {
          deviceType: settingsArg.type,
          boardRevision: settingsArg.board_revision,
          firmwareVersion: stringValue(infoArg.fw_version),
        },
      },
      settings: settingsArg,
      info: infoArg,
      entities: entitiesFor(settingsArg.type, infoArg),
      fetchedAt: new Date().toISOString(),
    };
  }

  private async commandForRequest(requestArg: IServiceCallRequest): Promise<ILektricoCommand> {
    const key = this.commandKey(requestArg);
    if (!key) {
      throw new LektricoClientError(`Lektrico service ${requestArg.service} requires a target entity id or data.key matching a supported entity.`);
    }

    if (requestArg.service === 'press') {
      const command = buttonCommands[key];
      if (!command) {
        throw new LektricoClientError(`Unsupported Lektrico button target: ${key}.`);
      }
      return command;
    }

    if (requestArg.service === 'set_value') {
      const value = numberValue(requestArg.data?.value ?? requestArg.data?.nativeValue ?? requestArg.data?.brightness);
      if (value === undefined) {
        throw new LektricoClientError('Lektrico set_value requires numeric data.value.');
      }
      if (key === 'led_max_brightness') {
        return { key, method: 'app_config.set', params: { config_key: 'led_max_brightness', config_value: value } };
      }
      if (key === 'dynamic_limit' || key === 'dynamic_current') {
        return { key, method: 'dynamic_current.set', params: { dynamic_current: value } };
      }
    }

    if (requestArg.service === 'select_option') {
      if (key !== 'load_balancing_mode') {
        throw new LektricoClientError(`Unsupported Lektrico select target: ${key}.`);
      }
      const option = stringValue(requestArg.data?.option ?? requestArg.data?.value);
      const optionIndex = option ? lbModeOptions.indexOf(option) : numberValue(requestArg.data?.index);
      if (optionIndex === undefined || optionIndex < 0 || optionIndex >= lbModeOptions.length) {
        throw new LektricoClientError(`Lektrico load_balancing_mode requires option ${lbModeOptions.join(', ')}.`);
      }
      return { key, method: 'app_config.set', params: { config_key: 'load_balancing_mode', config_value: optionIndex } };
    }

    if (requestArg.service === 'turn_on' || requestArg.service === 'turn_off' || requestArg.service === 'toggle') {
      const desired = requestArg.service === 'toggle' ? !(await this.booleanEntityState(key)) : requestArg.service === 'turn_on';
      if (key === 'authentication') {
        return { key, method: 'app_config.set', params: { config_key: 'headless', config_value: !desired } };
      }
      if (key === 'lock') {
        return { key, method: 'app_config.set', params: { config_key: 'charger_locked', config_value: desired } };
      }
      if (key === 'force_single_phase') {
        return { key, method: 'dynamic_current.Set', params: { dynamic_current: await this.dynamicCurrent(), relay_mode: desired ? 1 : 3 } };
      }
    }

    throw new LektricoClientError(`Unsupported Lektrico service or target: ${requestArg.service}.${key}.`);
  }

  private commandKey(requestArg: IServiceCallRequest): string | undefined {
    const direct = stringValue(requestArg.data?.key ?? requestArg.data?.entityKey ?? requestArg.data?.action ?? requestArg.data?.command ?? requestArg.data?.target);
    if (direct) {
      const normalized = slug(direct);
      const match = supportedCommandKeys.find((keyArg) => normalized === keyArg || normalized.includes(keyArg));
      if (match) {
        return match;
      }
    }

    const targetText = slug(`${requestArg.target.entityId || ''} ${requestArg.target.deviceId || ''}`);
    return supportedCommandKeys.find((keyArg) => targetText.includes(keyArg));
  }

  private async dynamicCurrent(): Promise<number> {
    const value = await this.entityState('dynamic_limit');
    const numeric = numberValue(value);
    if (numeric === undefined) {
      throw new LektricoClientError('Lektrico force_single_phase control requires a readable dynamic_limit value.');
    }
    return numeric;
  }

  private async booleanEntityState(keyArg: string): Promise<boolean> {
    const value = await this.entityState(keyArg);
    if (typeof value !== 'boolean') {
      throw new LektricoClientError(`Lektrico toggle requires a boolean current state for ${keyArg}.`);
    }
    return value;
  }

  private async entityState(keyArg: string): Promise<unknown> {
    const snapshot = await this.getSnapshot();
    return snapshot.entities.find((entityArg) => entityArg.id === keyArg || entityArg.attributes?.key === keyArg)?.state;
  }

  private async rpcGet(pathArg: string): Promise<Record<string, unknown>> {
    return this.requestJson(`/rpc/${pathArg}`, { method: 'GET' });
  }

  private async rpcPost(commandArg: ILektricoCommand): Promise<Record<string, unknown>> {
    return this.requestJson('/rpc', {
      method: 'POST',
      body: {
        src: 'HASS',
        id: Math.floor(10000000 + Math.random() * 90000000),
        method: commandArg.method,
        ...(commandArg.params ? { params: commandArg.params } : {}),
      },
    });
  }

  private async requestJson(pathArg: string, optionsArg: { method: 'GET' | 'POST'; body?: Record<string, unknown> }): Promise<Record<string, unknown>> {
    const endpoint = this.endpoint();
    if (!endpoint) {
      throw new LektricoClientError('Lektrico local HTTP requests require config.host or config.url.');
    }

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), this.timeoutMs());
    const portPart = endpoint.port && endpoint.port !== 80 ? `:${endpoint.port}` : '';
    const url = `http://${endpoint.host}${portPart}${pathArg.startsWith('/') ? pathArg : `/${pathArg}`}`;

    try {
      const response = await globalThis.fetch(url, {
        method: optionsArg.method,
        headers: {
          accept: 'application/json',
          ...(optionsArg.body ? { 'content-type': 'application/json' } : {}),
        },
        body: optionsArg.body ? JSON.stringify(optionsArg.body) : undefined,
        signal: controller.signal,
      });
      const text = await response.text();
      if (!response.ok) {
        throw new LektricoClientError(`Lektrico HTTP ${pathArg} failed with ${response.status}: ${text.slice(0, 160)}`);
      }
      const contentType = response.headers.get('content-type') || '';
      if (contentType && !contentType.includes('application/json')) {
        throw new LektricoClientError(`Lektrico HTTP ${pathArg} returned non-JSON content-type ${contentType}.`);
      }
      const parsed = text ? JSON.parse(text) as unknown : {};
      if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
        throw new LektricoClientError(`Lektrico HTTP ${pathArg} returned invalid JSON object.`);
      }
      return parsed as Record<string, unknown>;
    } catch (errorArg) {
      if (errorArg instanceof SyntaxError) {
        throw new LektricoClientError(`Lektrico HTTP ${pathArg} returned invalid JSON: ${errorArg.message}`);
      }
      throw errorArg;
    } finally {
      clearTimeout(timer);
    }
  }

  private hasLiveHttpConfig(): boolean {
    return Boolean(this.endpoint());
  }

  private endpoint(): ILektricoEndpoint | undefined {
    const configuredUrl = stringValue(this.configArg.url) || (typeof this.configArg.host === 'string' && this.configArg.host.includes('://') ? this.configArg.host : undefined);
    if (configuredUrl) {
      try {
        const url = new URL(configuredUrl);
        if (url.protocol !== 'http:') {
          return undefined;
        }
        return { protocol: 'http', host: url.hostname, port: url.port ? Number(url.port) : this.configArg.port || lektricoDefaultPort };
      } catch {
        return undefined;
      }
    }
    if (!this.configArg.host) {
      return undefined;
    }
    return { protocol: 'http', host: this.configArg.host, port: this.configArg.port || lektricoDefaultPort };
  }

  private timeoutMs(): number {
    return this.configArg.timeoutMs || 8000;
  }
}

const chargerStates = ['available', 'charging', 'connected', 'error', 'locked', 'need_auth', 'paused', 'paused_by_scheduler', 'updating_firmware'];
const currentLimitReasons = ['no_limit', 'installation_current', 'user_limit', 'dynamic_limit', 'schedule', 'em_offline', 'em', 'ocpp', 'overtemperature', 'switching_phases', 'user_limit', '1p_charging_disabled', 'unknown'];
const lbModeOptions = ['disabled', 'power', 'hybrid', 'green'];

const activeErrorDefaults = {
  state_e_activated: false,
  overtemp: false,
  critical_temp: false,
  overcurrent: false,
  meter_fault: false,
  undervoltage_error: false,
  overvoltage_error: false,
  rcd_error: false,
  cp_diode_failure: false,
  contactor_failure: false,
};

const buttonCommands: Record<string, ILektricoCommand> = {
  charge_start: { key: 'charge_start', method: 'charge.start', params: { tag: 'HASS' } },
  charge_stop: { key: 'charge_stop', method: 'charge.stop' },
  charging_schedule_override: { key: 'charging_schedule_override', method: 'Charging_schedule.Override' },
  reboot: { key: 'reboot', method: 'device.reset' },
};

const supportedCommandKeys = [
  'charge_start',
  'charge_stop',
  'charging_schedule_override',
  'reboot',
  'led_max_brightness',
  'dynamic_limit',
  'dynamic_current',
  'load_balancing_mode',
  'authentication',
  'lock',
  'force_single_phase',
];

const entitiesFor = (deviceTypeArg: TLektricoDeviceType, infoArg: Record<string, unknown>): ISimpleLocalEntitySnapshot[] => {
  const entities: ISimpleLocalEntitySnapshot[] = [];
  const charger = deviceTypeArg === lektricoDeviceTypes.charger1p || deviceTypeArg === lektricoDeviceTypes.charger3p;
  const threePhase = deviceTypeArg === lektricoDeviceTypes.charger3p || deviceTypeArg === lektricoDeviceTypes.energyMeter3p;

  const add = (entityArg: ISimpleLocalEntitySnapshot | undefined) => {
    if (entityArg && entityArg.state !== undefined) {
      entities.push(entityArg);
    }
  };
  const sensor = (idArg: string, nameArg: string, stateArg: unknown, unitArg?: string, attributesArg?: Record<string, unknown>) => add({ id: idArg, name: nameArg, platform: 'sensor', state: stateArg, unit: unitArg, attributes: { key: idArg, ...attributesArg } });
  const binary = (idArg: string, nameArg: string, stateArg: unknown) => add({ id: idArg, name: nameArg, platform: 'binary_sensor', state: Boolean(stateArg), attributes: { key: idArg, deviceClass: 'problem' } });
  const button = (idArg: string, nameArg: string) => add({ id: idArg, name: nameArg, platform: 'button', state: false, writable: true, attributes: { key: idArg } });
  const number = (idArg: string, nameArg: string, stateArg: unknown, unitArg: string, minArg: number, maxArg: number, stepArg: number) => add({ id: idArg, name: nameArg, platform: 'number', state: stateArg, unit: unitArg, writable: true, attributes: { key: idArg, min: minArg, max: maxArg, step: stepArg } });
  const switchEntity = (idArg: string, nameArg: string, stateArg: unknown) => add({ id: idArg, name: nameArg, platform: 'switch', state: Boolean(stateArg), writable: true, attributes: { key: idArg } });

  if (charger) {
    sensor('state', 'State', infoArg.charger_state, undefined, { deviceClass: 'enum', options: chargerStates });
    sensor('charging_time', 'Charging Time', infoArg.charging_time, 's', { deviceClass: 'duration' });
    sensor('power', 'Power', infoArg.instant_power, 'W', { deviceClass: 'power', stateClass: 'measurement' });
    sensor('energy', 'Energy', divideNumber(infoArg.session_energy, 1000), 'kWh', { deviceClass: 'energy' });
    sensor('temperature', 'Temperature', infoArg.temperature, 'C', { deviceClass: 'temperature', stateClass: 'measurement' });
    sensor('lifetime_energy', 'Lifetime Energy', infoArg.total_charged_energy, 'kWh', { deviceClass: 'energy', stateClass: 'total_increasing' });
    sensor('installation_current', 'Installation Current', infoArg.install_current, 'A', { deviceClass: 'current' });
    sensor('limit_reason', 'Limit Reason', infoArg.current_limit_reason, undefined, { deviceClass: 'enum', options: currentLimitReasons });
    binary('state_e_activated', 'State E Activated', infoArg.state_e_activated);
    binary('overtemp', 'Overtemp', infoArg.overtemp);
    binary('critical_temp', 'Critical Temp', infoArg.critical_temp);
    binary('overcurrent', 'Overcurrent', infoArg.overcurrent);
    binary('meter_fault', 'Meter Fault', infoArg.meter_fault);
    binary('undervoltage', 'Undervoltage', infoArg.undervoltage_error);
    binary('overvoltage', 'Overvoltage', infoArg.overvoltage_error);
    binary('rcd_error', 'RCD Error', infoArg.rcd_error);
    binary('cp_diode_failure', 'CP Diode Failure', infoArg.cp_diode_failure);
    binary('contactor_failure', 'Contactor Failure', infoArg.contactor_failure);
    button('charge_start', 'Charge Start');
    button('charge_stop', 'Charge Stop');
    button('charging_schedule_override', 'Charging Schedule Override');
    button('reboot', 'Reboot');
    number('led_max_brightness', 'LED Max Brightness', infoArg.led_max_brightness, '%', 0, 100, 5);
    number('dynamic_limit', 'Dynamic Limit', infoArg.dynamic_current, 'A', 0, 32, 1);
    switchEntity('authentication', 'Authentication', infoArg.require_auth);
    switchEntity('lock', 'Lock', infoArg.charger_state === 'locked');
    if (deviceTypeArg === lektricoDeviceTypes.charger3p) {
      switchEntity('force_single_phase', 'Force Single Phase', numberValue(infoArg.relay_mode) === 1);
    }
  } else {
    sensor('breaker_current', 'Breaker Current', infoArg.breaker_curent, 'A', { deviceClass: 'current', stateClass: 'measurement' });
    const lbModeIndex = numberValue(infoArg.lb_mode) ?? 0;
    add({ id: 'load_balancing_mode', name: 'Load Balancing Mode', platform: 'select', state: lbModeOptions[lbModeIndex] || String(lbModeIndex), writable: true, attributes: { key: 'load_balancing_mode', options: lbModeOptions, value: lbModeIndex } });
    button('reboot', 'Reboot');
  }

  if (threePhase) {
    sensor('voltage_l1', 'Voltage L1', infoArg.voltage_l1, 'V', { deviceClass: 'voltage' });
    sensor('voltage_l2', 'Voltage L2', infoArg.voltage_l2, 'V', { deviceClass: 'voltage' });
    sensor('voltage_l3', 'Voltage L3', infoArg.voltage_l3, 'V', { deviceClass: 'voltage' });
    sensor('current_l1', 'Current L1', infoArg.current_l1, 'A', { deviceClass: 'current', stateClass: 'measurement' });
    sensor('current_l2', 'Current L2', infoArg.current_l2, 'A', { deviceClass: 'current', stateClass: 'measurement' });
    sensor('current_l3', 'Current L3', infoArg.current_l3, 'A', { deviceClass: 'current', stateClass: 'measurement' });
  } else {
    sensor('voltage', 'Voltage', infoArg.voltage_l1, 'V', { deviceClass: 'voltage' });
    sensor('current', 'Current', infoArg.current_l1, 'A', { deviceClass: 'current', stateClass: 'measurement' });
  }

  if (!charger) {
    if (threePhase) {
      sensor('power_l1', 'Power L1', infoArg.power_l1, 'W', { deviceClass: 'power', stateClass: 'measurement' });
      sensor('power_l2', 'Power L2', infoArg.power_l2, 'W', { deviceClass: 'power', stateClass: 'measurement' });
      sensor('power_l3', 'Power L3', infoArg.power_l3, 'W', { deviceClass: 'power', stateClass: 'measurement' });
      sensor('pf_l1', 'Power Factor L1', multiplyNumber(infoArg.power_factor_l1, 100), '%', { deviceClass: 'power_factor', stateClass: 'measurement' });
      sensor('pf_l2', 'Power Factor L2', multiplyNumber(infoArg.power_factor_l2, 100), '%', { deviceClass: 'power_factor', stateClass: 'measurement' });
      sensor('pf_l3', 'Power Factor L3', multiplyNumber(infoArg.power_factor_l3, 100), '%', { deviceClass: 'power_factor', stateClass: 'measurement' });
    } else {
      sensor('power', 'Power', infoArg.power_l1, 'W', { deviceClass: 'power', stateClass: 'measurement' });
      sensor('pf', 'Power Factor', multiplyNumber(infoArg.power_factor_l1, 100), '%', { deviceClass: 'power_factor', stateClass: 'measurement' });
    }
  }

  return entities;
};

const deviceTypeFromId = (deviceIdArg: string): TLektricoDeviceType => {
  if (deviceIdArg.startsWith(lektricoDeviceTypes.charger1p)) {
    return lektricoDeviceTypes.charger1p;
  }
  if (deviceIdArg.startsWith(lektricoDeviceTypes.charger3p)) {
    return lektricoDeviceTypes.charger3p;
  }
  if (deviceIdArg.startsWith('m2w_81')) {
    return lektricoDeviceTypes.energyMeter1p;
  }
  if (deviceIdArg.startsWith('m2w_83')) {
    return lektricoDeviceTypes.energyMeter3p;
  }
  throw new LektricoClientError(`Unknown Lektrico device_id: ${deviceIdArg}.`);
};

const readableChargerState = (stateArg: string): string => {
  switch (stateArg) {
    case 'A':
      return 'available';
    case 'B':
      return 'connected';
    case 'B_AUTH':
      return 'need_auth';
    case 'B_PAUSE':
      return 'paused';
    case 'C':
    case 'D':
      return 'charging';
    case 'E':
    case 'F':
      return 'error';
    case 'OTA':
      return 'updating_firmware';
    case 'LOCKED':
      return 'locked';
    case 'B_SCHEDULER':
      return 'paused_by_scheduler';
    default:
      return stateArg;
  }
};

const currentLimitReason = (valueArg: unknown): string => {
  if (typeof valueArg === 'string' && !Number.isFinite(Number(valueArg))) {
    return valueArg;
  }
  const index = numberValue(valueArg) ?? 0;
  return currentLimitReasons[index] || currentLimitReasons[currentLimitReasons.length - 1];
};

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

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

const numberArray = (valueArg: unknown): number[] => Array.isArray(valueArg) ? valueArg.map((itemArg) => numberValue(itemArg) ?? 0) : [];
const divideNumber = (valueArg: unknown, divisorArg: number): number | undefined => {
  const value = numberValue(valueArg);
  return value === undefined ? undefined : value / divisorArg;
};
const multiplyNumber = (valueArg: unknown, multiplierArg: number): number | undefined => {
  const value = numberValue(valueArg);
  return value === undefined ? undefined : value * multiplierArg;
};
const slug = (valueArg: unknown): string => String(valueArg || '').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
const clone = <TValue>(valueArg: TValue): TValue => JSON.parse(JSON.stringify(valueArg)) as TValue;
const errorMessage = (errorArg: unknown): string => errorArg instanceof Error ? errorArg.message : String(errorArg);
