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 { IKwbConfig, IKwbRawData, IKwbSensorSnapshot } from './kwb.types.js';
import { kwbDefaultName, kwbDefaultPort, kwbProfile } from './kwb.types.js';

export class KwbMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IKwbConfig>, 'profile'>): ISimpleLocalSnapshot {
    const rawData = optionsArg.rawData ?? optionsArg.config.rawData;
    if (isKwbRawData(rawData)) {
      return this.toSnapshotFromKwbRaw(optionsArg.config, rawData, optionsArg.online, optionsArg.source, optionsArg.error);
    }
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: kwbProfile });
  }

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

  public static offlineSnapshot(configArg: IKwbConfig, errorArg: string): ISimpleLocalSnapshot {
    return this.toSnapshotFromKwbRaw(configArg, { sensors: [], source: 'runtime', fetchedAt: new Date().toISOString() }, false, 'runtime', errorArg);
  }

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

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

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

  private static toSnapshotFromKwbRaw(configArg: IKwbConfig, rawDataArg: IKwbRawData, onlineArg = true, sourceArg: ISimpleLocalSnapshot['source'] = 'manual', errorArg?: string): ISimpleLocalSnapshot {
    const host = configArg.host;
    const port = configArg.port || (host ? kwbDefaultPort : undefined);
    const source = rawDataArg.source === 'file' ? 'manual' : rawDataArg.source || sourceArg;
    const updatedAt = rawDataArg.fetchedAt || new Date().toISOString();
    const sensors = (rawDataArg.sensors || []).filter((sensorArg) => configArg.raw || sensorArg.kind !== 'raw');
    const entities: ISimpleLocalEntitySnapshot[] = sensors.map((sensorArg) => this.sensorEntity(sensorArg, onlineArg));

    return {
      device: {
        id: configArg.uniqueId || configArg.deviceId || (host ? `${host}:${port || ''}` : undefined) || configArg.name || 'kwb_easyfire',
        name: configArg.name || kwbDefaultName,
        manufacturer: 'KWB',
        model: 'Easyfire',
        host,
        port,
        protocol: configArg.filePath || configArg.packetFile ? 'local' : 'tcp',
        configurationUrl: host ? `tcp://${host}${port ? `:${port}` : ''}` : undefined,
        attributes: cleanAttributes({ source: rawDataArg.source || source, packetCount: rawDataArg.packets?.length, filePath: configArg.filePath || configArg.packetFile }),
      },
      entities: entities.length ? entities : [{ id: 'online', name: 'Online', platform: 'binary_sensor', state: onlineArg, available: true }],
      online: onlineArg,
      updatedAt,
      source,
      rawData: rawDataArg,
      error: errorArg,
      capabilities: {
        localRead: true,
        localControl: false,
      },
    };
  }

  private static sensorEntity(sensorArg: IKwbSensorSnapshot, onlineArg: boolean): ISimpleLocalEntitySnapshot {
    return {
      id: sensorArg.id || this.slug(sensorArg.name),
      name: sensorArg.name,
      platform: 'sensor',
      state: sensorArg.available ? sensorArg.value : null,
      available: onlineArg && sensorArg.available,
      unit: sensorArg.unit,
      deviceClass: sensorArg.kind === 'temperature' ? 'temperature' : undefined,
      stateClass: sensorArg.kind === 'temperature' ? 'measurement' : undefined,
      attributes: cleanAttributes({
        packet: sensorArg.packet,
        index: sensorArg.index,
        sensorType: sensorArg.kind,
      }),
    };
  }
}

const isKwbRawData = (valueArg: unknown): valueArg is IKwbRawData => {
  return Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) && (Array.isArray((valueArg as IKwbRawData).sensors) || Array.isArray((valueArg as IKwbRawData).packets)));
};

const cleanAttributes = (attributesArg: Record<string, unknown>): Record<string, unknown> | undefined => {
  const clean = Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  return Object.keys(clean).length ? clean : undefined;
};
