import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IApcupsdSnapshot } from './apcupsd.types.js';

const apcupsdDomain = 'apcupsd';

export class ApcupsdMapper {
  public static toDevices(snapshotArg: IApcupsdSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    return [{
      id: this.upsDeviceId(snapshotArg),
      integrationDomain: apcupsdDomain,
      name: this.upsName(snapshotArg),
      protocol: 'unknown',
      manufacturer: 'APC',
      model: snapshotArg.ups.model || 'UPS',
      online: snapshotArg.online,
      features: [
        { id: 'status', capability: 'sensor', name: 'Status', readable: true, writable: false },
        { id: 'line_online', capability: 'sensor', name: 'Online status', readable: true, writable: false },
        { id: 'battery_charge', capability: 'sensor', name: 'Battery charge', readable: true, writable: false, unit: '%' },
        { id: 'battery_runtime', capability: 'sensor', name: 'Battery runtime', readable: true, writable: false, unit: 'min' },
        { id: 'load', capability: 'sensor', name: 'Load', readable: true, writable: false, unit: '%' },
        { id: 'line_voltage', capability: 'sensor', name: 'Input voltage', readable: true, writable: false, unit: 'V' },
        { id: 'output_voltage', capability: 'sensor', name: 'Output voltage', readable: true, writable: false, unit: 'V' },
        { id: 'nominal_power', capability: 'sensor', name: 'Nominal output power', readable: true, writable: false, unit: 'W' },
      ],
      state: [
        { featureId: 'status', value: snapshotArg.ups.status || null, updatedAt },
        { featureId: 'line_online', value: snapshotArg.ups.lineOnline ?? null, updatedAt },
        { featureId: 'battery_charge', value: snapshotArg.battery.chargePercent ?? null, updatedAt },
        { featureId: 'battery_runtime', value: snapshotArg.battery.timeLeftMinutes ?? null, updatedAt },
        { featureId: 'load', value: snapshotArg.power.loadPercent ?? null, updatedAt },
        { featureId: 'line_voltage', value: snapshotArg.power.lineVoltage ?? null, updatedAt },
        { featureId: 'output_voltage', value: snapshotArg.power.outputVoltage ?? null, updatedAt },
        { featureId: 'nominal_power', value: snapshotArg.power.nominalPowerWatts ?? null, updatedAt },
      ],
      metadata: this.cleanAttributes({
        host: snapshotArg.ups.host,
        port: snapshotArg.ups.port,
        serialNumber: snapshotArg.ups.serialNumber,
        firmware: snapshotArg.ups.firmware,
        version: snapshotArg.ups.version,
        hostname: snapshotArg.ups.hostname,
        mode: snapshotArg.ups.mode,
        source: snapshotArg.source,
        statusKeys: Object.keys(snapshotArg.status).sort(),
        error: snapshotArg.error,
      }),
    }];
  }

  public static toEntities(snapshotArg: IApcupsdSnapshot): IIntegrationEntity[] {
    const deviceId = this.upsDeviceId(snapshotArg);
    const uniqueBase = this.uniqueBase(snapshotArg);
    const baseName = this.upsName(snapshotArg);
    const available = snapshotArg.online;
    const entities: IIntegrationEntity[] = [];

    entities.push(this.entity(snapshotArg, 'sensor', 'status', `${baseName} Status`, snapshotArg.ups.status || 'unknown', {
      deviceClass: 'enum',
      statusFlag: snapshotArg.ups.statusFlag,
      mode: snapshotArg.ups.mode,
      lastTransfer: snapshotArg.power.lastTransfer,
      selfTest: snapshotArg.battery.selfTest,
      lastSelfTest: snapshotArg.battery.lastSelfTest,
      statusKeys: Object.keys(snapshotArg.status).sort(),
    }, deviceId, uniqueBase, available));

    if (snapshotArg.ups.lineOnline !== undefined) {
      entities.push(this.entity(snapshotArg, 'binary_sensor', 'online_status', `${baseName} Online Status`, this.binaryState(snapshotArg.ups.lineOnline), {
        statusFlag: snapshotArg.ups.statusFlag,
        deviceClass: 'power',
      }, deviceId, uniqueBase, available));
    }

    this.pushNumberEntity(entities, snapshotArg, 'battery_charge', `${baseName} Battery Charge`, snapshotArg.battery.chargePercent, '%', deviceId, uniqueBase, available, { deviceClass: 'battery', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'battery_voltage', `${baseName} Battery Voltage`, snapshotArg.battery.voltage, 'V', deviceId, uniqueBase, available, { deviceClass: 'voltage', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'battery_nominal_voltage', `${baseName} Battery Nominal Voltage`, snapshotArg.battery.nominalVoltage, 'V', deviceId, uniqueBase, available, { deviceClass: 'voltage', entityCategory: 'diagnostic' });
    this.pushNumberEntity(entities, snapshotArg, 'time_left', `${baseName} Time Left`, snapshotArg.battery.timeLeftMinutes, 'min', deviceId, uniqueBase, available, { deviceClass: 'duration', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'time_on_battery', `${baseName} Time On Battery`, snapshotArg.battery.timeOnBatterySeconds, 's', deviceId, uniqueBase, available, { deviceClass: 'duration', stateClass: 'total_increasing' });
    this.pushNumberEntity(entities, snapshotArg, 'total_time_on_battery', `${baseName} Total Time On Battery`, snapshotArg.battery.totalTimeOnBatterySeconds, 's', deviceId, uniqueBase, available, { deviceClass: 'duration', stateClass: 'total_increasing' });
    this.pushStringEntity(entities, snapshotArg, 'battery_status', `${baseName} Battery Status`, snapshotArg.battery.status, deviceId, uniqueBase, available);

    this.pushNumberEntity(entities, snapshotArg, 'load', `${baseName} Load`, snapshotArg.power.loadPercent, '%', deviceId, uniqueBase, available, { stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'input_voltage', `${baseName} Input Voltage`, snapshotArg.power.lineVoltage, 'V', deviceId, uniqueBase, available, { deviceClass: 'voltage', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'output_voltage', `${baseName} Output Voltage`, snapshotArg.power.outputVoltage, 'V', deviceId, uniqueBase, available, { deviceClass: 'voltage', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'line_frequency', `${baseName} Line Frequency`, snapshotArg.power.lineFrequency, 'Hz', deviceId, uniqueBase, available, { deviceClass: 'frequency', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'nominal_output_power', `${baseName} Nominal Output Power`, snapshotArg.power.nominalPowerWatts, 'W', deviceId, uniqueBase, available, { deviceClass: 'power', entityCategory: 'diagnostic' });
    this.pushNumberEntity(entities, snapshotArg, 'nominal_apparent_power', `${baseName} Nominal Apparent Power`, snapshotArg.power.nominalApparentPowerVa, 'VA', deviceId, uniqueBase, available, { deviceClass: 'apparent_power', entityCategory: 'diagnostic' });
    this.pushNumberEntity(entities, snapshotArg, 'output_current', `${baseName} Output Current`, snapshotArg.power.outputCurrentAmps, 'A', deviceId, uniqueBase, available, { deviceClass: 'current', stateClass: 'measurement' });
    this.pushNumberEntity(entities, snapshotArg, 'transfer_count', `${baseName} Transfer Count`, snapshotArg.power.transferCount, undefined, deviceId, uniqueBase, available, { stateClass: 'total_increasing' });

    return entities;
  }

  public static upsDeviceId(snapshotArg: IApcupsdSnapshot): string {
    return `apcupsd.ups.${this.uniqueBase(snapshotArg)}`;
  }

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

  private static pushNumberEntity(entitiesArg: IIntegrationEntity[], snapshotArg: IApcupsdSnapshot, keyArg: string, nameArg: string, valueArg: number | undefined, unitArg: string | undefined, deviceIdArg: string, uniqueBaseArg: string, availableArg: boolean, attributesArg: Record<string, unknown> = {}): void {
    if (valueArg === undefined) {
      return;
    }
    entitiesArg.push(this.entity(snapshotArg, 'sensor', keyArg, nameArg, valueArg, {
      unit: unitArg,
      ...attributesArg,
    }, deviceIdArg, uniqueBaseArg, availableArg));
  }

  private static pushStringEntity(entitiesArg: IIntegrationEntity[], snapshotArg: IApcupsdSnapshot, keyArg: string, nameArg: string, valueArg: string | undefined, deviceIdArg: string, uniqueBaseArg: string, availableArg: boolean, attributesArg: Record<string, unknown> = {}): void {
    if (!valueArg) {
      return;
    }
    entitiesArg.push(this.entity(snapshotArg, 'sensor', keyArg, nameArg, valueArg, attributesArg, deviceIdArg, uniqueBaseArg, availableArg));
  }

  private static entity(snapshotArg: IApcupsdSnapshot, platformArg: IIntegrationEntity['platform'], keyArg: string, nameArg: string, stateArg: unknown, attributesArg: Record<string, unknown>, deviceIdArg: string, uniqueBaseArg: string, availableArg: boolean): IIntegrationEntity {
    void snapshotArg;
    return {
      id: `${platformArg}.${this.slug(nameArg)}`,
      uniqueId: `apcupsd_${uniqueBaseArg}_${keyArg}`,
      integrationDomain: apcupsdDomain,
      deviceId: deviceIdArg,
      platform: platformArg,
      name: nameArg,
      state: stateArg,
      attributes: this.cleanAttributes(attributesArg),
      available: availableArg,
    };
  }

  private static upsName(snapshotArg: IApcupsdSnapshot): string {
    return snapshotArg.ups.name || snapshotArg.ups.hostname || snapshotArg.ups.model || 'APC UPS';
  }

  private static uniqueBase(snapshotArg: IApcupsdSnapshot): string {
    return this.slug(snapshotArg.ups.serialNumber || snapshotArg.ups.id || snapshotArg.ups.host || this.upsName(snapshotArg));
  }

  private static binaryState(valueArg: boolean | undefined): 'on' | 'off' | 'unknown' {
    return valueArg === undefined ? 'unknown' : valueArg ? 'on' : 'off';
  }

  private static cleanAttributes(attributesArg: Record<string, unknown>): Record<string, unknown> {
    return Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  }
}
