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 { IIntellifireConfig, IIntellifirePollData, TIntellifireRawData } from './intellifire.types.js';
import { intellifireDefaultName, intellifireDefaultPort, intellifireProfile } from './intellifire.types.js';

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

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

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

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

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

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

    const poll = objectValue(rawObject.poll) || rawObject;
    if (!isPollData(poll)) {
      return parsedRawData;
    }

    const serial = stringValue(poll.serial);
    const host = configArg.host || configArg.ipAddress || stringValue(poll.ipv4_address);
    const port = configArg.port || (host ? intellifireDefaultPort : undefined);
    const name = configArg.name || normalizedName(stringValue(poll.name)) || intellifireDefaultName;
    const deviceId = configArg.uniqueId || configArg.deviceId || serial || (host ? `${host}:${port || intellifireDefaultPort}` : undefined) || intellifireDefaultName;
    const entities = entitiesFromPoll(poll);

    return {
      device: {
        id: deviceId,
        name,
        manufacturer: 'Hearth and Home',
        model: 'IFT-WFM',
        serialNumber: serial,
        host,
        port,
        protocol: 'http',
      },
      entities,
      poll,
      fetchedAt: stringValue(rawObject.fetchedAt) || new Date().toISOString(),
    };
  }
}

const intellifireErrorDefinitions: Array<{ code: number; id: string; name: string }> = [
  { code: 2, id: 'error_pilot_flame', name: 'Pilot Flame Error' },
  { code: 130, id: 'error_pilot_flame', name: 'Pilot Flame Error' },
  { code: 6, id: 'error_fan_delay', name: 'Fan Delay Error' },
  { code: 4, id: 'error_flame', name: 'Flame Error' },
  { code: 64, id: 'error_maintenance', name: 'Maintenance Error' },
  { code: 129, id: 'error_disabled', name: 'Disabled Error' },
  { code: 145, id: 'error_disabled', name: 'Disabled Error' },
  { code: 132, id: 'error_fan', name: 'Fan Error' },
  { code: 133, id: 'error_lights', name: 'Lights Error' },
  { code: 134, id: 'error_accessory', name: 'Accessory Error' },
  { code: 144, id: 'error_soft_lock_out', name: 'Soft Lock Out Error' },
  { code: 3269, id: 'error_ecm_offline', name: 'ECM Offline Error' },
  { code: 642, id: 'error_offline', name: 'Offline Error' },
];

const entitiesFromPoll = (pollArg: Record<string, unknown>): ISimpleLocalEntitySnapshot[] => {
  const entities: ISimpleLocalEntitySnapshot[] = [];
  const errors = arrayNumbers(pollArg.errors);
  const flameHeight = numberValue(pollArg.height);
  const temperature = numberValue(pollArg.temperature);
  const setpoint = numberValue(pollArg.setpoint);
  const fanSpeed = numberValue(pollArg.fanspeed);
  const lightLevel = numberValue(pollArg.light);
  const hasFan = booleanValue(pollArg.feature_fan) ?? fanSpeed !== undefined;
  const hasLight = booleanValue(pollArg.feature_light) ?? lightLevel !== undefined;
  const hasThermostat = booleanValue(pollArg.feature_thermostat) ?? setpoint !== undefined;
  const isOn = booleanValue(pollArg.power) ?? false;
  const pilotOn = booleanValue(pollArg.pilot) ?? false;
  const thermostatOn = booleanValue(pollArg.thermostat) ?? false;

  binaryEntity(entities, 'on_off', 'Flame', isOn);
  binaryEntity(entities, 'timer_on', 'Timer On', booleanValue(pollArg.timer));
  binaryEntity(entities, 'pilot_light_on', 'Pilot Light On', pilotOn);
  binaryEntity(entities, 'thermostat_on', 'Thermostat On', thermostatOn);
  binaryEntity(entities, 'hot', 'Hot', booleanValue(pollArg.hot));

  for (const definition of intellifireErrorDefinitions) {
    if (!entities.some((entityArg) => entityArg.id === definition.id)) {
      binaryEntity(entities, definition.id, definition.name, errors.includes(definition.code), { entityCategory: 'diagnostic', problem: true });
    }
  }

  sensorEntity(entities, 'flame_height', 'Flame Height', flameHeight === undefined ? undefined : flameHeight + 1);
  sensorEntity(entities, 'temperature', 'Temperature', temperature, { unit: '°C', deviceClass: 'temperature', stateClass: 'measurement' });
  sensorEntity(entities, 'target_temp', 'Target Temperature', setpoint === undefined ? undefined : setpoint / 100, { unit: '°C', deviceClass: 'temperature', stateClass: 'measurement' });
  sensorEntity(entities, 'fan_speed', 'Fan Speed', fanSpeed, { stateClass: 'measurement' });
  sensorEntity(entities, 'timer_remaining', 'Timer Remaining', numberValue(pollArg.timeremaining), { unit: 's', deviceClass: 'duration' });
  sensorEntity(entities, 'connection_quality', 'Connection Quality', numberValue(pollArg.remote_connection_quality), { entityCategory: 'diagnostic' });
  sensorEntity(entities, 'ecm_latency', 'ECM Latency', numberValue(pollArg.ecm_latency), { entityCategory: 'diagnostic' });
  sensorEntity(entities, 'uptime', 'Uptime', numberValue(pollArg.remote_uptime), { unit: 's', entityCategory: 'diagnostic' });
  sensorEntity(entities, 'downtime', 'Downtime', numberValue(pollArg.remote_downtime), { unit: 's', entityCategory: 'diagnostic' });
  sensorEntity(entities, 'ipv4_address', 'IPv4 Address', stringValue(pollArg.ipv4_address), { entityCategory: 'diagnostic' });
  sensorEntity(entities, 'firmware_version', 'Firmware Version', stringValue(pollArg.firmware_version_string) || stringValue(pollArg.firmware_version), { entityCategory: 'diagnostic' });

  entities.push({ id: 'flame_switch', name: 'Flame', platform: 'switch', state: isOn, writable: true, attributes: { key: 'power' } });
  entities.push({ id: 'pilot_switch', name: 'Pilot Light', platform: 'switch', state: pilotOn, writable: true, attributes: { key: 'pilot' } });
  if (flameHeight !== undefined) {
    entities.push({ id: 'flame_control', name: 'Flame Control', platform: 'number', state: flameHeight + 1, writable: true, attributes: { key: 'height', min: 1, max: 5, step: 1 } });
  }
  if (hasFan) {
    entities.push({ id: 'fan', name: 'Fan', platform: 'fan', state: (fanSpeed || 0) > 0, writable: true, attributes: { key: 'fanspeed', speed: fanSpeed ?? 0, speedCount: 4 } });
  }
  if (hasLight) {
    entities.push({ id: 'lights', name: 'Lights', platform: 'light', state: (lightLevel || 0) > 0, writable: true, attributes: { key: 'light', level: lightLevel ?? 0, brightness: (lightLevel ?? 0) * 85 } });
  }
  if (hasThermostat) {
    entities.push({
      id: 'climate',
      name: 'Thermostat',
      platform: 'climate',
      state: thermostatOn ? 'heat' : 'off',
      writable: true,
      attributes: {
        key: 'thermostat',
        currentTemperature: temperature,
        targetTemperature: setpoint === undefined ? undefined : setpoint / 100,
        temperatureUnit: '°C',
      },
    });
  }

  return entities;
};

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

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

const isPollData = (valueArg: Record<string, unknown>): valueArg is IIntellifirePollData => {
  return ['serial', 'power', 'height', 'temperature', 'firmware_version', 'feature_fan', 'feature_light', 'feature_thermostat'].some((keyArg) => keyArg in valueArg);
};

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 unknown;
  } 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 booleanValue = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'number') {
    return valueArg !== 0;
  }
  if (typeof valueArg === 'string' && valueArg.trim()) {
    const normalized = valueArg.trim().toLowerCase();
    if (['1', 'true', 'on', 'yes'].includes(normalized)) {
      return true;
    }
    if (['0', 'false', 'off', 'no'].includes(normalized)) {
      return false;
    }
  }
  return undefined;
};

const arrayNumbers = (valueArg: unknown): number[] => {
  return Array.isArray(valueArg) ? valueArg.map((itemArg) => numberValue(itemArg)).filter((itemArg): itemArg is number => itemArg !== undefined) : [];
};

const normalizedName = (valueArg: string | undefined): string | undefined => {
  return valueArg && valueArg !== 'unset' && valueArg !== 'undefined' ? valueArg : undefined;
};
