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 { IIometerConfig, IIometerRawApiData, IIometerReadingResponse, IIometerRegister, IIometerStatusResponse, TIometerRawData } from './iometer.types.js';
import { iometerDefaultName, iometerDefaultPort, iometerProfile } from './iometer.types.js';

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

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

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

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

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

  public static normalizeRawData(rawDataArg: unknown, configArg: IIometerConfig): unknown {
    const parsedRawData = parseMaybeJson(rawDataArg);
    const rawObject = objectValue(parsedRawData);
    if (!rawObject || Array.isArray(rawObject.entities) || rawObject.device) {
      return parsedRawData;
    }

    const reading = readingValue(rawObject);
    const status = statusValue(rawObject);
    if (!reading && !status) {
      return parsedRawData;
    }

    const meterNumber = stringValue(status?.meter?.number) || stringValue(reading?.meter?.number);
    const device = status?.device;
    const host = configArg.host || hostFromUrl(configArg.url);
    const port = configArg.port || (host ? iometerDefaultPort : undefined);
    const name = configArg.name || (meterNumber ? `IOmeter ${meterNumber}` : iometerDefaultName);
    const entities = entitiesFromApi(reading, status);

    return {
      device: {
        id: configArg.uniqueId || configArg.deviceId || stringValue(device?.id) || (host ? `${host}:${port || iometerDefaultPort}` : undefined) || meterNumber || iometerDefaultName,
        name,
        manufacturer: 'IOmeter',
        model: 'IOmeter bridge',
        serialNumber: stringValue(device?.id),
        host,
        port,
        protocol: 'http',
      },
      entities,
      reading,
      status,
      metrics: metricsFromReading(reading),
      fetchedAt: stringValue(rawObject.fetchedAt) || new Date().toISOString(),
    };
  }
}

const readingObis = {
  totalConsumption: '01-00:01.08.00*ff',
  totalProduction: '01-00:02.08.00*ff',
  currentPower: '01-00:10.07.00*ff',
  currentPowerAlt: '01-00:24.07.00*ff',
  consumptionTariffT1: '01-00:01.08.01*ff',
  consumptionTariffT2: '01-00:01.08.02*ff',
};

const entitiesFromApi = (readingArg?: IIometerReadingResponse, statusArg?: IIometerStatusResponse): ISimpleLocalEntitySnapshot[] => {
  const entities: ISimpleLocalEntitySnapshot[] = [];
  const metrics = metricsFromReading(readingArg);
  const meterNumber = stringValue(statusArg?.meter?.number) || stringValue(readingArg?.meter?.number);
  const bridge = statusArg?.device?.bridge;
  const core = statusArg?.device?.core;

  sensorEntity(entities, 'meter_number', 'Meter Number', meterNumber, { icon: 'mdi:meter-electric' });
  sensorEntity(entities, 'wifi_rssi', 'Wi-Fi RSSI', numberValue(bridge?.rssi), { unit: 'dB', deviceClass: 'signal_strength', stateClass: 'measurement', entityCategory: 'diagnostic' });
  sensorEntity(entities, 'core_bridge_rssi', 'Core Bridge RSSI', numberValue(core?.rssi), { unit: 'dB', deviceClass: 'signal_strength', stateClass: 'measurement', entityCategory: 'diagnostic' });
  sensorEntity(entities, 'power_status', 'Power Status', stringValue(core?.powerStatus) || undefined, { deviceClass: 'enum' });
  sensorEntity(entities, 'battery_level', 'Battery Level', numberValue(core?.batteryLevel), { unit: '%', deviceClass: 'battery', stateClass: 'measurement' });
  sensorEntity(entities, 'pin_status', 'PIN Status', stringValue(core?.pinStatus) || undefined, { deviceClass: 'enum' });
  sensorEntity(entities, 'consumption_tariff_t1', 'Consumption Tariff T1', metrics.consumptionTariffT1, { unit: 'Wh', deviceClass: 'energy', stateClass: 'total' });
  sensorEntity(entities, 'consumption_tariff_t2', 'Consumption Tariff T2', metrics.consumptionTariffT2, { unit: 'Wh', deviceClass: 'energy', stateClass: 'total' });
  sensorEntity(entities, 'total_consumption', 'Total Consumption', metrics.totalConsumption, { unit: 'Wh', deviceClass: 'energy', stateClass: 'total' });
  sensorEntity(entities, 'total_production', 'Total Production', metrics.totalProduction, { unit: 'Wh', deviceClass: 'energy', stateClass: 'total' });
  sensorEntity(entities, 'power', 'Power', metrics.currentPower, { unit: 'W', deviceClass: 'power', stateClass: 'measurement' });
  sensorEntity(entities, 'bridge_version', 'Bridge Version', stringValue(bridge?.version), { entityCategory: 'diagnostic' });
  sensorEntity(entities, 'core_version', 'Core Version', stringValue(core?.version), { entityCategory: 'diagnostic' });

  binaryEntity(entities, 'connection_status', 'Connection Status', core?.connectionStatus === undefined || core.connectionStatus === null ? undefined : core.connectionStatus === 'connected');
  binaryEntity(entities, 'attachment_status', 'Attachment Status', core?.attachmentStatus === undefined || core.attachmentStatus === null ? undefined : core.attachmentStatus === 'attached');
  return entities;
};

const metricsFromReading = (readingArg?: IIometerReadingResponse): Record<string, number | undefined> => {
  return {
    consumptionTariffT1: registerValue(readingArg, readingObis.consumptionTariffT1),
    consumptionTariffT2: registerValue(readingArg, readingObis.consumptionTariffT2),
    totalConsumption: registerValue(readingArg, readingObis.totalConsumption),
    totalProduction: registerValue(readingArg, readingObis.totalProduction),
    currentPower: registerValue(readingArg, readingObis.currentPower) ?? registerValue(readingArg, readingObis.currentPowerAlt),
  };
};

const registerValue = (readingArg: IIometerReadingResponse | undefined, obisArg: string): number | undefined => {
  const registers = Array.isArray(readingArg?.meter?.reading?.registers) ? readingArg.meter.reading.registers : [];
  return numberValue(registers.find((registerArg: IIometerRegister) => registerArg.obis === obisArg)?.value);
};

const sensorEntity = (entitiesArg: ISimpleLocalEntitySnapshot[], idArg: string, nameArg: string, stateArg: unknown, attributesArg: Record<string, unknown> = {}): void => {
  if (stateArg === undefined || stateArg === null || stateArg === '') {
    return;
  }
  entitiesArg.push({ id: idArg, name: nameArg, platform: 'sensor', state: stateArg, attributes: attributesArg });
};

const binaryEntity = (entitiesArg: ISimpleLocalEntitySnapshot[], idArg: string, nameArg: string, stateArg: boolean | undefined): void => {
  if (stateArg === undefined) {
    return;
  }
  entitiesArg.push({ id: idArg, name: nameArg, platform: 'binary_sensor', state: stateArg, attributes: { deviceClass: 'connectivity' } });
};

const readingValue = (rawObjectArg: Record<string, unknown>): IIometerReadingResponse | undefined => {
  const candidate = objectValue(rawObjectArg.reading) || (objectValue(rawObjectArg.meter)?.reading ? rawObjectArg : undefined);
  return candidate as IIometerReadingResponse | undefined;
};

const statusValue = (rawObjectArg: Record<string, unknown>): IIometerStatusResponse | undefined => {
  const candidate = objectValue(rawObjectArg.status) || (objectValue(rawObjectArg.device)?.core ? rawObjectArg : undefined);
  return candidate as IIometerStatusResponse | undefined;
};

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

const parseMaybeJson = (valueArg: unknown): unknown => {
  if (typeof valueArg !== 'string') {
    return valueArg;
  }
  try {
    return JSON.parse(valueArg) as IIometerRawApiData;
  } catch {
    return valueArg;
  }
};

const stringValue = (valueArg: unknown): string | undefined => {
  return 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()) {
    const parsed = Number(valueArg.trim());
    return Number.isFinite(parsed) ? parsed : undefined;
  }
  return undefined;
};

const hostFromUrl = (urlArg: string | undefined): string | undefined => {
  if (!urlArg) {
    return undefined;
  }
  try {
    return new URL(urlArg.includes('://') ? urlArg : `http://${urlArg}`).hostname;
  } catch {
    return undefined;
  }
};
