import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions, type TSimpleLocalRawData } from '../../core/index.js';
import type { IEdl21Config } from './edl21.types.js';
import { edl21DefaultName, edl21Profile } from './edl21.types.js';

interface IEdl21SensorDescription {
  name: string;
  translationKey: string;
  deviceClass?: string;
  stateClass?: string;
  entityRegistryEnabledDefault?: boolean;
}

const sensorDescriptions: Record<string, IEdl21SensorDescription> = {
  '1-0:0.0.0*255': { name: 'Ownership ID', translationKey: 'ownership_id', entityRegistryEnabledDefault: false },
  '1-0:0.0.9*255': { name: 'Electricity ID', translationKey: 'electricity_id' },
  '1-0:0.2.0*0': { name: 'Configuration program version number', translationKey: 'configuration_program_version_number' },
  '1-0:0.2.0*1': { name: 'Firmware version number', translationKey: 'firmware_version_number' },
  '1-0:1.7.0*255': { name: 'Positive active instantaneous power', translationKey: 'positive_active_instantaneous_power', deviceClass: 'power', stateClass: 'measurement' },
  '1-0:1.8.0*255': { name: 'Positive active energy total', translationKey: 'positive_active_energy_total', deviceClass: 'energy', stateClass: 'total_increasing' },
  '1-0:1.8.1*255': { name: 'Positive active energy in tariff T1', translationKey: 'positive_active_energy_tariff_t1', deviceClass: 'energy', stateClass: 'total_increasing' },
  '1-0:1.8.2*255': { name: 'Positive active energy in tariff T2', translationKey: 'positive_active_energy_tariff_t2', deviceClass: 'energy', stateClass: 'total_increasing' },
  '1-0:1.17.0*255': { name: 'Last signed positive active energy total', translationKey: 'last_signed_positive_active_energy_total' },
  '1-0:2.8.0*255': { name: 'Negative active energy total', translationKey: 'negative_active_energy_total', deviceClass: 'energy', stateClass: 'total_increasing' },
  '1-0:2.8.1*255': { name: 'Negative active energy in tariff T1', translationKey: 'negative_active_energy_tariff_t1', deviceClass: 'energy', stateClass: 'total_increasing' },
  '1-0:2.8.2*255': { name: 'Negative active energy in tariff T2', translationKey: 'negative_active_energy_tariff_t2', deviceClass: 'energy', stateClass: 'total_increasing' },
  '1-0:14.7.0*255': { name: 'Supply frequency', translationKey: 'supply_frequency' },
  '1-0:15.7.0*255': { name: 'Absolute active instantaneous power', translationKey: 'absolute_active_instantaneous_power', deviceClass: 'power', stateClass: 'measurement' },
  '1-0:16.7.0*255': { name: 'Sum active instantaneous power', translationKey: 'sum_active_instantaneous_power', deviceClass: 'power', stateClass: 'measurement' },
  '1-0:31.7.0*255': { name: 'L1 active instantaneous amperage', translationKey: 'l1_active_instantaneous_amperage', deviceClass: 'current', stateClass: 'measurement' },
  '1-0:32.7.0*255': { name: 'L1 active instantaneous voltage', translationKey: 'l1_active_instantaneous_voltage', deviceClass: 'voltage', stateClass: 'measurement' },
  '1-0:36.7.0*255': { name: 'L1 active instantaneous power', translationKey: 'l1_active_instantaneous_power', deviceClass: 'power', stateClass: 'measurement' },
  '1-0:51.7.0*255': { name: 'L2 active instantaneous amperage', translationKey: 'l2_active_instantaneous_amperage', deviceClass: 'current', stateClass: 'measurement' },
  '1-0:52.7.0*255': { name: 'L2 active instantaneous voltage', translationKey: 'l2_active_instantaneous_voltage', deviceClass: 'voltage', stateClass: 'measurement' },
  '1-0:56.7.0*255': { name: 'L2 active instantaneous power', translationKey: 'l2_active_instantaneous_power', deviceClass: 'power', stateClass: 'measurement' },
  '1-0:71.7.0*255': { name: 'L3 active instantaneous amperage', translationKey: 'l3_active_instantaneous_amperage', deviceClass: 'current', stateClass: 'measurement' },
  '1-0:72.7.0*255': { name: 'L3 active instantaneous voltage', translationKey: 'l3_active_instantaneous_voltage', deviceClass: 'voltage', stateClass: 'measurement' },
  '1-0:76.7.0*255': { name: 'L3 active instantaneous power', translationKey: 'l3_active_instantaneous_power', deviceClass: 'power', stateClass: 'measurement' },
  '1-0:81.7.1*255': { name: 'U(L2)/U(L1) phase angle', translationKey: 'u_l2_u_l1_phase_angle' },
  '1-0:81.7.2*255': { name: 'U(L3)/U(L1) phase angle', translationKey: 'u_l3_u_l1_phase_angle' },
  '1-0:81.7.4*255': { name: 'U(L1)/I(L1) phase angle', translationKey: 'u_l1_i_l1_phase_angle' },
  '1-0:81.7.15*255': { name: 'U(L2)/I(L2) phase angle', translationKey: 'u_l2_i_l2_phase_angle' },
  '1-0:81.7.26*255': { name: 'U(L3)/I(L3) phase angle', translationKey: 'u_l3_i_l3_phase_angle' },
  '1-0:96.1.0*255': { name: 'Metering point ID 1', translationKey: 'metering_point_id_1' },
  '1-0:96.5.0*255': { name: 'Internal operating status', translationKey: 'internal_operating_status' },
};

const unitMapping: Record<string, string> = {
  Wh: 'Wh',
  kWh: 'kWh',
  W: 'W',
  A: 'A',
  V: 'V',
  '\u00b0': '\u00b0',
  Hz: 'Hz',
};

export class Edl21Mapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IEdl21Config>, 'profile'>): ISimpleLocalSnapshot {
    return SimpleLocalMapper.toSnapshot({
      ...optionsArg,
      profile: edl21Profile,
      rawData: this.normalizeRawData(optionsArg.config, optionsArg.rawData),
    });
  }

  public static toSnapshotFromRaw(configArg: IEdl21Config, rawDataArg: TSimpleLocalRawData): ISimpleLocalSnapshot {
    return this.toSnapshot({ config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

  public static toDevices(snapshotArg: ISimpleLocalSnapshot): shxInterfaces.data.IDeviceDefinition[] {
    return SimpleLocalMapper.toDevices(edl21Profile, snapshotArg);
  }

  public static toEntities(snapshotArg: ISimpleLocalSnapshot): IIntegrationEntity[] {
    return SimpleLocalMapper.toEntities(edl21Profile, snapshotArg);
  }

  public static slug(valueArg: unknown): string {
    return SimpleLocalMapper.slug(valueArg);
  }

  private static normalizeRawData(configArg: IEdl21Config, rawDataArg: unknown): unknown {
    if (!rawDataArg || ('device' in Object(rawDataArg) && 'entities' in Object(rawDataArg))) {
      return rawDataArg;
    }

    const rawObject = isRecord(rawDataArg) ? rawDataArg : undefined;
    const telegrams = Array.isArray(rawDataArg) ? rawDataArg : arrayValue(rawObject?.valList) || arrayValue(rawObject?.telegrams);
    if (!telegrams?.length) {
      return rawDataArg;
    }

    const serialPort = configArg.serialPort || configArg.serial_port || stringValue(rawObject?.serial_port) || stringValue(rawObject?.serialPort);
    const electricityId = compactId(stringValue(rawObject?.serverId) || stringValue(rawObject?.electricityId) || stringValue(rawObject?.electricity_id) || configArg.uniqueId || serialPort || edl21Profile.domain);
    const entities = telegrams.map((telegramArg) => this.entityFromTelegram(electricityId, telegramArg)).filter((entityArg): entityArg is ISimpleLocalEntitySnapshot => Boolean(entityArg));
    if (!entities.length) {
      return rawDataArg;
    }

    return {
      device: {
        id: configArg.uniqueId || electricityId,
        name: configArg.name || edl21DefaultName,
        manufacturer: edl21Profile.displayName,
        model: edl21Profile.model,
        serialNumber: electricityId,
        protocol: edl21Profile.defaultProtocol,
        attributes: {
          serialPort,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }

  private static entityFromTelegram(electricityIdArg: string, telegramArg: unknown): ISimpleLocalEntitySnapshot | undefined {
    if (!isRecord(telegramArg)) {
      return undefined;
    }

    const obis = stringValue(telegramArg.objName) || stringValue(telegramArg.obis);
    if (!obis) {
      return undefined;
    }

    const description = sensorDescriptions[obis];
    if (!description) {
      return undefined;
    }

    return {
      id: SimpleLocalMapper.slug(obis),
      uniqueId: `${edl21Profile.domain}_${SimpleLocalMapper.slug(electricityIdArg)}_${SimpleLocalMapper.slug(obis)}`,
      name: description.name,
      platform: 'sensor',
      state: telegramArg.value,
      available: true,
      writable: false,
      unit: unitValue(telegramArg.unit),
      deviceClass: description.deviceClass,
      stateClass: description.stateClass,
      attributes: {
        obis,
        translationKey: description.translationKey,
        entityRegistryEnabledDefault: description.entityRegistryEnabledDefault,
        rawUnit: telegramArg.unit,
      },
    };
  }
}

const unitValue = (valueArg: unknown): string | undefined => {
  if (valueArg === undefined || valueArg === null || valueArg === 0) {
    return undefined;
  }
  const value = String(valueArg).trim();
  return value ? unitMapping[value] || value : undefined;
};

const compactId = (valueArg: string): string => valueArg.replace(/\s+/g, '') || edl21Profile.domain;

const arrayValue = (valueArg: unknown): unknown[] | undefined => Array.isArray(valueArg) ? valueArg : undefined;

const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg));

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