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 { IGoalzeroConfig } from './goalzero.types.js';
import { goalzeroDefaultName, goalzeroProfile } from './goalzero.types.js';

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

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

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

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

  private static normalizeRawData(configArg: IGoalzeroConfig, rawDataArg: unknown): unknown {
    if (isSnapshotRecord(rawDataArg)) {
      return rawDataArg;
    }

    const rawObject = isRecord(rawDataArg) ? rawDataArg : undefined;
    if (!rawObject) {
      return rawDataArg;
    }

    const sysdata = recordValue(rawObject.sysdata) || recordValue(rawObject.sysData) || rawObject;
    const data = recordValue(rawObject.data) || rawObject;
    if (!hasAnyKey(data, [...sensorKeys, ...binarySensorKeys, ...switchKeys])) {
      return rawDataArg;
    }

    const entities: ISimpleLocalEntitySnapshot[] = [];
    for (const key of sensorKeys) {
      if (!(key in data)) {
        continue;
      }
      const description = sensorDescriptions[key];
      entities.push({
        id: SimpleLocalMapper.slug(key),
        name: description.name,
        platform: 'sensor',
        state: data[key],
        available: true,
        unit: description.unit,
        deviceClass: description.deviceClass,
        stateClass: description.stateClass,
        attributes: {
          entityCategory: description.entityCategory,
          entityRegistryEnabledDefault: description.entityRegistryEnabledDefault,
        },
      });
    }

    for (const key of binarySensorKeys) {
      if (!(key in data)) {
        continue;
      }
      const description = binarySensorDescriptions[key];
      entities.push({
        id: SimpleLocalMapper.slug(key),
        name: description.name,
        platform: 'binary_sensor',
        state: booleanState(data[key]),
        available: true,
        deviceClass: description.deviceClass,
        attributes: {
          entityCategory: description.entityCategory,
        },
      });
    }

    for (const key of switchKeys) {
      if (!(key in data)) {
        continue;
      }
      entities.push({
        id: SimpleLocalMapper.slug(key),
        name: switchDescriptions[key],
        platform: 'switch',
        state: booleanState(data[key]),
        available: true,
        writable: true,
      });
    }

    const macAddress = stringValue(sysdata?.macAddress ?? sysdata?.mac_address);
    const name = configArg.name || stringValue(rawObject.name) || goalzeroDefaultName;
    return {
      device: {
        id: configArg.uniqueId || macAddress || configArg.host || name,
        name,
        manufacturer: goalzeroProfile.manufacturer,
        model: stringValue(sysdata?.model) || goalzeroProfile.model,
        host: configArg.host || stringValue(data.ipAddr),
        protocol: configArg.host ? 'http' : 'local',
        serialNumber: macAddress,
        attributes: {
          firmwareVersion: stringValue(data.firmwareVersion),
          macAddress,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const sensorDescriptions: Record<string, { name: string; unit?: string; deviceClass?: string; stateClass?: string; entityCategory?: string; entityRegistryEnabledDefault?: boolean }> = {
  wattsIn: { name: 'Power in', unit: 'W', deviceClass: 'power', stateClass: 'measurement' },
  ampsIn: { name: 'Current in', unit: 'A', deviceClass: 'current', stateClass: 'measurement', entityRegistryEnabledDefault: false },
  wattsOut: { name: 'Power out', unit: 'W', deviceClass: 'power', stateClass: 'measurement' },
  ampsOut: { name: 'Current out', unit: 'A', deviceClass: 'current', stateClass: 'measurement', entityRegistryEnabledDefault: false },
  whOut: { name: 'Energy out', unit: 'Wh', deviceClass: 'energy', stateClass: 'total_increasing', entityRegistryEnabledDefault: false },
  whStored: { name: 'Energy stored', unit: 'Wh', deviceClass: 'energy', stateClass: 'total' },
  volts: { name: 'Voltage', unit: 'V', deviceClass: 'voltage', entityRegistryEnabledDefault: false },
  socPercent: { name: 'State of charge percent', unit: '%', deviceClass: 'battery' },
  timeToEmptyFull: { name: 'Time to empty/full', unit: 'min', deviceClass: 'duration' },
  temperature: { name: 'Temperature', unit: 'C', deviceClass: 'temperature', entityCategory: 'diagnostic' },
  wifiStrength: { name: 'Wi-Fi strength', unit: 'dB', deviceClass: 'signal_strength', entityRegistryEnabledDefault: false, entityCategory: 'diagnostic' },
  timestamp: { name: 'Total run time', unit: 's', deviceClass: 'duration', entityRegistryEnabledDefault: false, entityCategory: 'diagnostic' },
  ssid: { name: 'Wi-Fi SSID', entityRegistryEnabledDefault: false, entityCategory: 'diagnostic' },
  ipAddr: { name: 'IP address', entityRegistryEnabledDefault: false, entityCategory: 'diagnostic' },
  firmwareVersion: { name: 'Firmware version', entityCategory: 'diagnostic' },
};

const binarySensorDescriptions: Record<string, { name: string; deviceClass?: string; entityCategory?: string }> = {
  backlight: { name: 'Backlight' },
  app_online: { name: 'App online', deviceClass: 'connectivity', entityCategory: 'diagnostic' },
  isCharging: { name: 'Charging', deviceClass: 'battery_charging' },
  inputDetected: { name: 'Input detected', deviceClass: 'power' },
};

const switchDescriptions: Record<string, string> = {
  v12PortStatus: '12V port status',
  usbPortStatus: 'USB port status',
  acPortStatus: 'AC port status',
};

const sensorKeys = Object.keys(sensorDescriptions);
const binarySensorKeys = Object.keys(binarySensorDescriptions);
const switchKeys = Object.keys(switchDescriptions);

const isSnapshotRecord = (valueArg: unknown): valueArg is ISimpleLocalSnapshot => Boolean(valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'entities' in valueArg);

const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg));

const recordValue = (valueArg: unknown): Record<string, unknown> | undefined => isRecord(valueArg) ? valueArg : undefined;

const stringValue = (valueArg: unknown): string | undefined => typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;

const booleanState = (valueArg: unknown): boolean => valueArg === true || valueArg === 1 || valueArg === '1' || valueArg === 'on';

const hasAnyKey = (recordArg: Record<string, unknown>, keysArg: string[]): boolean => keysArg.some((keyArg) => keyArg in recordArg);
