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 { IHeatmiserConfig, IHeatmiserThermostatConfig } from './heatmiser.types.js';
import { heatmiserProfile } from './heatmiser.types.js';

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

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

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

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

  private static normalizeRawData(configArg: IHeatmiserConfig, rawDataArg: unknown): unknown {
    if (!isRecord(rawDataArg) && !Array.isArray(rawDataArg)) {
      return rawDataArg;
    }
    if (isRecord(rawDataArg) && 'device' in rawDataArg && 'entities' in rawDataArg) {
      return rawDataArg;
    }

    const thermostats = thermostatRecords(rawDataArg);
    if (!thermostats.length) {
      return rawDataArg;
    }

    return {
      device: {
        id: configArg.uniqueId || (configArg.host ? `${configArg.host}:${configArg.port || ''}` : undefined) || heatmiserProfile.domain,
        name: configArg.name || heatmiserProfile.defaultName,
        manufacturer: 'Heatmiser',
        model: 'UH1 V3 thermostat hub',
        host: configArg.host,
        port: configArg.port,
        protocol: 'tcp',
      },
      entities: thermostats.map((recordArg, indexArg) => thermostatEntity(configArg, recordArg, indexArg)),
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const thermostatRecords = (rawDataArg: unknown): Record<string, unknown>[] => {
  if (Array.isArray(rawDataArg)) {
    return rawDataArg.filter(isRecord);
  }
  if (!isRecord(rawDataArg)) {
    return [];
  }
  const thermostats = rawDataArg.thermostats ?? rawDataArg.tstats ?? rawDataArg.devices;
  if (Array.isArray(thermostats)) {
    return thermostats.filter(isRecord);
  }
  return hasThermostatState(rawDataArg) ? [rawDataArg] : [];
};

const thermostatEntity = (configArg: IHeatmiserConfig, recordArg: Record<string, unknown>, indexArg: number): ISimpleLocalEntitySnapshot => {
  const id = numberValue(recordArg.id) ?? numberValue(recordArg.deviceId ?? recordArg.device_id) ?? indexArg + 1;
  const configured = thermostatConfig(configArg, id);
  const currentTemperature = numberValue(recordArg.currentTemperature ?? recordArg.current_temperature ?? recordArg.floorTemp ?? recordArg.floor_temp);
  const targetTemperature = numberValue(recordArg.targetTemperature ?? recordArg.target_temperature ?? recordArg.targetTemp ?? recordArg.target_temp);
  const temperatureUnit = stringValue(recordArg.temperatureUnit ?? recordArg.temperature_unit ?? recordArg.temperatureFormat ?? recordArg.temperature_format) || 'C';
  return {
    id: `thermostat_${id}`,
    name: stringValue(recordArg.name) || configured?.name || `Thermostat ${id}`,
    platform: 'climate',
    state: hvacMode(recordArg.currentState ?? recordArg.current_state ?? recordArg.hvacMode ?? recordArg.hvac_mode ?? recordArg.state),
    writable: true,
    attributes: {
      thermostatId: id,
      currentTemperature,
      targetTemperature,
      temperatureUnit,
      hvacModes: ['heat', 'off'],
      supportedFeatures: ['target_temperature', 'turn_on', 'turn_off'],
    },
  };
};

const thermostatConfig = (configArg: IHeatmiserConfig, idArg: number): IHeatmiserThermostatConfig | undefined => (configArg.tstats || configArg.thermostats || []).find((thermostatArg) => thermostatArg.id === idArg);

const hasThermostatState = (valueArg: Record<string, unknown>): boolean => ['currentTemperature', 'current_temperature', 'floorTemp', 'floor_temp', 'targetTemperature', 'target_temperature', 'targetTemp', 'target_temp', 'currentState', 'current_state'].some((keyArg) => keyArg in valueArg);

const hvacMode = (valueArg: unknown): string => {
  if (typeof valueArg === 'number') {
    return valueArg === 0 ? 'off' : 'heat';
  }
  const value = stringValue(valueArg)?.toLowerCase();
  if (value === '0' || value === 'off') {
    return 'off';
  }
  return 'heat';
};

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;
};
