import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions } from '../../core/index.js';
import type { IMillConfig, IMillNativeRawData, TMillRawData } from './mill.types.js';
import { millDefaultName, millDefaultPort, millProfile } from './mill.types.js';

export class MillMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IMillConfig>, 'profile'>): ISimpleLocalSnapshot {
    const rawData = optionsArg.rawData ?? optionsArg.config.rawData;
    if (this.isNativeRawData(rawData)) {
      return this.toSnapshotFromNativeRaw(optionsArg.config, rawData, optionsArg.online ?? true, optionsArg.source || 'http', optionsArg.error);
    }
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: millProfile });
  }

  public static toSnapshotFromRaw(configArg: IMillConfig, rawDataArg: TMillRawData): ISimpleLocalSnapshot {
    if (this.isNativeRawData(rawDataArg)) {
      return this.toSnapshotFromNativeRaw(configArg, rawDataArg, true, 'manual');
    }
    return SimpleLocalMapper.toSnapshot({ profile: millProfile, config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

  public static toSnapshotFromNativeRaw(configArg: IMillConfig, rawDataArg: IMillNativeRawData, onlineArg = true, sourceArg: ISimpleLocalSnapshot['source'] = 'http', errorArg?: string): ISimpleLocalSnapshot {
    const status = rawDataArg.status || {};
    const controlStatus = rawDataArg.controlStatus || {};
    const endpoint = this.endpoint(configArg);
    const macAddress = stringValue(status.mac_address) || configArg.macAddress;
    const name = configArg.name || stringValue(status.name) || endpoint.host || millDefaultName;
    const hvacMode = this.hvacMode(controlStatus.operation_mode);
    const currentPower = numberValue(controlStatus.current_power) ?? 0;
    const canControl = this.hasCommandTransport(configArg);
    const entities: ISimpleLocalSnapshot['entities'] = [{
      id: 'heater',
      uniqueId: `${millProfile.domain}_${this.slug(macAddress || endpoint.host || name)}_heater`,
      name,
      platform: 'climate',
      state: hvacMode,
      available: onlineArg,
      writable: canControl,
      unit: '°C',
      attributes: cleanAttributes({
        targetTemperature: numberValue(controlStatus.set_temperature),
        currentTemperature: numberValue(controlStatus.ambient_temperature),
        operationMode: stringValue(controlStatus.operation_mode),
        hvacAction: hvacMode === 'off' ? 'off' : currentPower > 0 ? 'heating' : 'idle',
        currentPower,
      }),
    }];

    for (const definition of millLocalSensorDefinitions) {
      const state = controlStatus[definition.key];
      if (state === undefined || state === null) {
        continue;
      }
      entities.push({
        id: definition.key,
        uniqueId: `${millProfile.domain}_${this.slug(macAddress || endpoint.host || name)}_${this.slug(definition.key)}`,
        name: `${name} ${definition.name}`,
        platform: 'sensor',
        state,
        available: onlineArg,
        writable: false,
        unit: definition.unit,
        deviceClass: definition.deviceClass,
        stateClass: 'measurement',
      });
    }

    return {
      device: {
        id: configArg.uniqueId || configArg.deviceId || macAddress || (endpoint.host ? `${endpoint.host}:${endpoint.port}` : undefined) || millProfile.domain,
        name,
        manufacturer: 'Mill',
        model: stringValue(status.model) || 'Generation 3',
        serialNumber: stringValue(status.serial_number),
        host: endpoint.host,
        port: endpoint.port,
        protocol: endpoint.protocol,
        configurationUrl: endpoint.host ? `${endpoint.protocol}://${endpoint.host}${endpoint.port && endpoint.port !== millDefaultPort ? `:${endpoint.port}` : ''}` : undefined,
        attributes: cleanAttributes({
          macAddress,
          version: stringValue(status.version),
        }),
      },
      entities,
      online: onlineArg,
      updatedAt: rawDataArg.fetchedAt || new Date().toISOString(),
      source: sourceArg,
      rawData: rawDataArg,
      error: errorArg,
      capabilities: {
        localRead: true,
        localControl: canControl,
        liveControlRequiresExecutor: !this.hasLiveEndpoint(configArg),
        protocol: 'mill-local-http',
      },
    };
  }

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

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

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

  public static isNativeRawData(valueArg: unknown): valueArg is IMillNativeRawData {
    return Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) && ('status' in valueArg || 'controlStatus' in valueArg));
  }

  public static hvacMode(valueArg: unknown): 'heat' | 'auto' | 'off' {
    const value = stringValue(valueArg);
    if (value === 'OFF') {
      return 'off';
    }
    if (value === 'Weekly program') {
      return 'auto';
    }
    return 'heat';
  }

  private static hasCommandTransport(configArg: IMillConfig): boolean {
    return this.hasLiveEndpoint(configArg) || Boolean(configArg.client?.execute || configArg.commandExecutor);
  }

  private static hasLiveEndpoint(configArg: IMillConfig): boolean {
    return Boolean(configArg.host || configArg.url || configArg.ipAddress);
  }

  private static endpoint(configArg: IMillConfig): { host?: string; port?: number; protocol: 'http' | 'https' } {
    const configured = configArg.url || configArg.host || configArg.ipAddress;
    if (configured && /^https?:\/\//i.test(configured)) {
      try {
        const url = new URL(configured);
        return { host: url.hostname, port: configArg.port || (url.port ? Number(url.port) : undefined) || millDefaultPort, protocol: url.protocol === 'https:' ? 'https' : 'http' };
      } catch {
        return { host: configured, port: configArg.port || millDefaultPort, protocol: 'http' };
      }
    }
    return { host: configured, port: configArg.port || millDefaultPort, protocol: configArg.useTls ? 'https' : 'http' };
  }
}

const millLocalSensorDefinitions: Array<{ key: string; name: string; unit?: string; deviceClass?: string }> = [
  { key: 'control_signal', name: 'Control Signal', unit: '%' },
  { key: 'current_power', name: 'Current Power', unit: 'W', deviceClass: 'power' },
  { key: 'raw_ambient_temperature', name: 'Uncalibrated Temperature', unit: '°C', deviceClass: 'temperature' },
];

const stringValue = (valueArg: unknown): string | undefined => {
  if (typeof valueArg === 'string' && valueArg.trim()) {
    return valueArg.trim();
  }
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
    return String(valueArg);
  }
  return 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);
    return Number.isFinite(parsed) ? parsed : undefined;
  }
  return undefined;
};

const cleanAttributes = (attributesArg: Record<string, unknown | undefined>): Record<string, unknown> | undefined => {
  const clean = Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  return Object.keys(clean).length ? clean : undefined;
};
