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 { IEgardiaConfig } from './egardia.types.js';
import { egardiaDefaultName, egardiaDefaultPort, egardiaDefaultVersion, egardiaProfile } from './egardia.types.js';

const alarmStateMap: Record<string, string> = {
  ARM: 'armed_away',
  'DAY HOME': 'armed_home',
  DISARM: 'disarmed',
  ARMHOME: 'armed_home',
  HOME: 'armed_home',
  'NIGHT HOME': 'armed_night',
  TRIGGERED: 'triggered',
};

const sensorDeviceClassMap: Record<string, string> = {
  'IR Sensor': 'motion',
  'Door Contact': 'opening',
  IR: 'motion',
};

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

  public static toSnapshotFromRaw(configArg: IEgardiaConfig, 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(egardiaProfile, snapshotArg);
  }

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

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

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

    const state = alarmState(rawDataArg.state ?? rawDataArg.status ?? rawDataArg.alarm_state ?? rawDataArg.alarmState);
    const sensors = sensorRecords(rawDataArg.sensors ?? rawDataArg.egardia_sensor ?? rawDataArg.devices);
    if (state === undefined && !sensors.length) {
      return rawDataArg;
    }

    const host = configArg.host || stringValue(rawDataArg.host);
    const port = configArg.port || numberValue(rawDataArg.port) || (host ? egardiaDefaultPort : undefined);
    const version = configArg.version || stringValue(rawDataArg.version) || egardiaDefaultVersion;
    const name = configArg.name || stringValue(rawDataArg.name) || egardiaDefaultName;
    const sensorStateMap = recordValue(rawDataArg.sensorStates) || recordValue(rawDataArg.sensor_states) || {};
    const entities: ISimpleLocalEntitySnapshot[] = [];

    if (state !== undefined) {
      entities.push({
        id: 'alarm_state',
        uniqueId: `${egardiaProfile.domain}_${SimpleLocalMapper.slug(host || name)}_alarm_state`,
        name: `${name} Alarm State`,
        platform: 'sensor',
        state,
        available: true,
        writable: false,
        attributes: {
          serviceDomain: 'alarm_control_panel',
          supportedControls: egardiaProfile.controlServices,
        },
      });
    }

    for (const sensor of sensors) {
      const id = stringValue(sensor.id) || stringValue(sensor.sensor_id) || stringValue(sensor.name);
      if (!id) {
        continue;
      }
      const sensorState = booleanState(sensor.state ?? sensor.status ?? sensor.is_on ?? sensor.isOn ?? sensorStateMap[id]);
      const type = stringValue(sensor.type);
      entities.push({
        id: `sensor_${SimpleLocalMapper.slug(id)}`,
        uniqueId: `${egardiaProfile.domain}_${SimpleLocalMapper.slug(host || name)}_${SimpleLocalMapper.slug(id)}`,
        name: stringValue(sensor.name) || id,
        platform: 'binary_sensor',
        state: sensorState ?? null,
        available: sensorState !== undefined,
        writable: false,
        deviceClass: type ? sensorDeviceClassMap[type] : undefined,
        attributes: {
          sensorId: id,
          type,
        },
      });
    }

    return {
      device: {
        id: configArg.uniqueId || (host ? `${host}:${port || ''}` : undefined) || name,
        name,
        manufacturer: egardiaProfile.manufacturer,
        model: version,
        host,
        port,
        protocol: egardiaProfile.defaultProtocol,
        attributes: {
          reportServerEnabled: configArg.reportServerEnabled ?? configArg.report_server_enabled,
          reportServerPort: configArg.reportServerPort ?? configArg.report_server_port,
          version,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const alarmState = (valueArg: unknown): string | undefined => {
  const value = stringValue(valueArg);
  if (!value) {
    return undefined;
  }
  return alarmStateMap[value.toUpperCase()] || value.toLowerCase();
};

const sensorRecords = (valueArg: unknown): Array<Record<string, unknown>> => {
  if (Array.isArray(valueArg)) {
    return valueArg.filter(isRecord);
  }
  if (isRecord(valueArg)) {
    return Object.values(valueArg).filter(isRecord);
  }
  return [];
};

const booleanState = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'number') {
    return valueArg !== 0;
  }
  const value = stringValue(valueArg)?.toLowerCase();
  if (!value) {
    return undefined;
  }
  if (['1', 'true', 'on', 'open', 'opened', 'triggered', 'motion'].includes(value)) {
    return true;
  }
  if (['0', 'false', 'off', 'closed', 'close', 'idle'].includes(value)) {
    return false;
  }
  return undefined;
};

const recordValue = (valueArg: unknown): Record<string, unknown> | undefined => isRecord(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;

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