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 { IGreeConfig } from './gree.types.js';
import { greeDefaultName, greeDefaultPort, greeProfile } from './gree.types.js';

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

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

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

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

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

    const deviceInfo = recordValue(rawDataArg.device_info) || recordValue(rawDataArg.deviceInfo) || recordValue(rawDataArg.info) || rawDataArg;
    const properties = recordValue(rawDataArg.raw_properties) || recordValue(rawDataArg.rawProperties) || recordValue(rawDataArg.properties) || recordValue(rawDataArg.state) || rawDataArg;
    const mac = configArg.macAddress || stringValue(deviceInfo.mac) || stringValue(rawDataArg.mac) || stringValue(deviceInfo.macAddress) || configArg.uniqueId;
    const host = configArg.host || stringValue(deviceInfo.ip) || stringValue(deviceInfo.host) || stringValue(rawDataArg.host);
    const port = configArg.port || numberValue(deviceInfo.port) || numberValue(rawDataArg.port) || (host ? greeDefaultPort : undefined);
    const name = configArg.name || stringValue(deviceInfo.name) || stringValue(rawDataArg.name) || greeDefaultName;
    const hasHints = Boolean(mac || host || 'Pow' in properties || 'power' in properties || 'SetTem' in properties || 'target_temperature' in properties);
    if (!hasHints) {
      return rawDataArg;
    }

    const uniqueBase = mac || (host ? `${host}:${port || ''}` : undefined) || name;
    const entities: ISimpleLocalEntitySnapshot[] = [climateEntity(uniqueBase, properties)];
    entities.push(...switchEntities(uniqueBase, properties));

    return {
      device: {
        id: uniqueBase,
        name,
        manufacturer: greeProfile.manufacturer,
        model: stringValue(deviceInfo.model) || greeProfile.model,
        serialNumber: mac,
        host,
        port,
        protocol: 'local',
        attributes: {
          macAddress: mac,
          bindStatus: stringValue(rawDataArg.bind_status) || stringValue(rawDataArg.bindStatus),
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const climateEntity = (uniqueBaseArg: string, propertiesArg: Record<string, unknown>): ISimpleLocalEntitySnapshot => {
  const power = booleanValue(propertiesArg.power ?? propertiesArg.Pow ?? propertiesArg.pwr);
  const hvacMode = power === false ? 'off' : hvacModeValue(propertiesArg.mode ?? propertiesArg.Mod ?? propertiesArg.hvac_mode ?? propertiesArg.hvacMode);
  const fanMode = fanModeValue(propertiesArg.fan_speed ?? propertiesArg.fanSpeed ?? propertiesArg.WdSpd);
  const presetMode = presetModeValue(propertiesArg);
  const swingMode = swingModeValue(propertiesArg);

  return {
    id: 'climate',
    uniqueId: `${greeProfile.domain}_${SimpleLocalMapper.slug(uniqueBaseArg)}_climate`,
    name: 'Climate',
    platform: 'climate',
    state: hvacMode || 'auto',
    available: true,
    writable: true,
    attributes: {
      currentTemperature: numberValue(propertiesArg.current_temperature ?? propertiesArg.currentTemperature ?? propertiesArg.TemSen ?? propertiesArg.env_temp),
      targetTemperature: numberValue(propertiesArg.target_temperature ?? propertiesArg.targetTemperature ?? propertiesArg.SetTem),
      temperatureUnit: temperatureUnitValue(propertiesArg.temperature_units ?? propertiesArg.temperatureUnits ?? propertiesArg.TemUn),
      hvacModes: ['auto', 'cool', 'dry', 'fan_only', 'heat', 'off'],
      hvacMode,
      fanMode,
      fanModes: ['auto', 'low', 'medium low', 'medium', 'medium high', 'high'],
      presetMode,
      presetModes: ['eco', 'away', 'boost', 'none', 'sleep'],
      swingMode,
      swingModes: ['off', 'vertical', 'horizontal', 'both'],
      serviceDomain: 'climate',
    },
  };
};

const switchEntities = (uniqueBaseArg: string, propertiesArg: Record<string, unknown>): ISimpleLocalEntitySnapshot[] => [
  switchEntity(uniqueBaseArg, 'light', 'Panel light', propertiesArg.light ?? propertiesArg.Lig),
  switchEntity(uniqueBaseArg, 'quiet', 'Quiet mode', propertiesArg.quiet ?? propertiesArg.Quiet),
  switchEntity(uniqueBaseArg, 'fresh_air', 'Fresh air', propertiesArg.fresh_air ?? propertiesArg.freshAir ?? propertiesArg.Air),
  switchEntity(uniqueBaseArg, 'xfan', 'Xtra fan', propertiesArg.xfan ?? propertiesArg.Blo),
  switchEntity(uniqueBaseArg, 'health_mode', 'Health mode', propertiesArg.anion ?? propertiesArg.health_mode ?? propertiesArg.healthMode ?? propertiesArg.Health),
].filter((entityArg): entityArg is ISimpleLocalEntitySnapshot => Boolean(entityArg));

const switchEntity = (uniqueBaseArg: string, idArg: string, nameArg: string, valueArg: unknown): ISimpleLocalEntitySnapshot | undefined => {
  const state = booleanValue(valueArg);
  if (state === undefined) {
    return undefined;
  }
  return {
    id: idArg,
    uniqueId: `${greeProfile.domain}_${SimpleLocalMapper.slug(uniqueBaseArg)}_${SimpleLocalMapper.slug(idArg)}`,
    name: nameArg,
    platform: 'switch',
    state,
    available: true,
    writable: true,
    attributes: {
      serviceDomain: 'switch',
    },
  };
};

const hvacModeValue = (valueArg: unknown): string | undefined => {
  const numeric = numberValue(valueArg);
  if (numeric !== undefined) {
    return ['auto', 'cool', 'dry', 'fan_only', 'heat'][numeric];
  }
  const value = stringValue(valueArg)?.toLowerCase();
  if (!value) return undefined;
  if (value === 'fan') return 'fan_only';
  if (['auto', 'cool', 'dry', 'fan_only', 'heat', 'off'].includes(value)) return value;
  return undefined;
};

const fanModeValue = (valueArg: unknown): string | undefined => {
  const numeric = numberValue(valueArg);
  if (numeric !== undefined) {
    return ['auto', 'low', 'medium low', 'medium', 'medium high', 'high'][numeric];
  }
  const value = stringValue(valueArg)?.toLowerCase();
  return value && ['auto', 'low', 'medium low', 'medium', 'medium high', 'high'].includes(value) ? value : undefined;
};

const presetModeValue = (propertiesArg: Record<string, unknown>): string => {
  if (booleanValue(propertiesArg.steady_heat ?? propertiesArg.steadyHeat ?? propertiesArg.StHt)) return 'away';
  if (booleanValue(propertiesArg.power_save ?? propertiesArg.powerSave ?? propertiesArg.SvSt)) return 'eco';
  if (booleanValue(propertiesArg.sleep ?? propertiesArg.SwhSlp)) return 'sleep';
  if (booleanValue(propertiesArg.turbo ?? propertiesArg.Tur)) return 'boost';
  return 'none';
};

const swingModeValue = (propertiesArg: Record<string, unknown>): string => {
  const horizontal = fullSwing(propertiesArg.horizontal_swing ?? propertiesArg.horizontalSwing ?? propertiesArg.SwingLfRig);
  const vertical = fullSwing(propertiesArg.vertical_swing ?? propertiesArg.verticalSwing ?? propertiesArg.SwUpDn);
  if (horizontal && vertical) return 'both';
  if (horizontal) return 'horizontal';
  if (vertical) return 'vertical';
  return 'off';
};

const fullSwing = (valueArg: unknown): boolean => {
  if (typeof valueArg === 'number') return valueArg > 0;
  const value = stringValue(valueArg)?.toLowerCase();
  return Boolean(value && ['fullswing', 'full_swing', 'swing', 'on', 'true', '1'].includes(value));
};

const temperatureUnitValue = (valueArg: unknown): string => {
  const value = stringValue(valueArg)?.toUpperCase();
  if (value === 'F' || value === 'FAHRENHEIT' || valueArg === 1) return 'F';
  return 'C';
};

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' && valueArg.trim() ? Number(valueArg) : undefined;
  return value !== undefined && Number.isFinite(value) ? value : undefined;
};
const booleanValue = (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', 'yes', 'on', 'enabled'].includes(value)) return true;
  if (['0', 'false', 'no', 'off', 'disabled'].includes(value)) return false;
  return undefined;
};
