import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type { IXiaomiMiioClientCommand, IXiaomiMiioConfig, IXiaomiMiioDeviceState, IXiaomiMiioEntityDescriptor, IXiaomiMiioEvent, IXiaomiMiioProperty, IXiaomiMiioSnapshot, IXiaomiMiioStateRecord, TXiaomiMiioDeviceKind } from './xiaomi_miio.types.js';

const defaultMiioPort = 54321;
const sensorUnits: Record<string, string> = {
  temperature: 'C',
  humidity: '%',
  target_humidity: '%',
  water_level: '%',
  battery: '%',
  battery_level: '%',
  pm25: 'ug/m3',
  pm25_2: 'ug/m3',
  pm10_density: 'ug/m3',
  tvoc: 'ug/m3',
  co2: 'ppm',
  load_power: 'W',
  motor_speed: 'rpm',
  motor2_speed: 'rpm',
  actual_speed: 'rpm',
  favorite_speed: 'rpm',
  clean_area: 'm2',
  area: 'm2',
  clean_time: 's',
  duration: 's',
  filter_life_remaining: '%',
  filter_hours_used: 'h',
  filter_left_time: 'd',
};

const primaryControlKeys = new Set(['is_on', 'on', 'power', 'brightness', 'color_temperature', 'rgb', 'hs_color', 'position', 'current_position', 'target_position', 'state', 'status', 'mode']);
const writableKeys = new Set(['is_on', 'on', 'power', 'brightness', 'color_temperature', 'rgb', 'hs_color', 'percentage', 'speed', 'fan_level', 'favorite_level', 'favorite_rpm', 'motor_speed', 'target_humidity', 'mode', 'position', 'target_position', 'oscillate', 'child_lock', 'buzzer', 'led', 'display', 'dry']);
const numberControlKeys = new Set(['fan_level', 'favorite_level', 'favorite_rpm', 'motor_speed', 'target_humidity', 'delay_off_countdown', 'led_brightness', 'led_brightness_level', 'volume', 'angle']);
const sensorKeys = new Set(['temperature', 'humidity', 'target_humidity', 'water_level', 'battery', 'battery_level', 'pm25', 'pm25_2', 'pm10_density', 'aqi', 'air_quality', 'tvoc', 'co2', 'load_power', 'motor_speed', 'motor2_speed', 'actual_speed', 'control_speed', 'favorite_speed', 'filter_life_remaining', 'filter_hours_used', 'filter_left_time', 'purify_volume', 'illuminance', 'illuminance_lux', 'clean_area', 'clean_time', 'duration', 'error']);
const topLevelStateKeys = [...primaryControlKeys, ...sensorKeys, 'state_code', 'fan_speed', 'fanspeed', 'oscillate', 'oscillating', 'preset_mode', 'current_humidity'];

export class XiaomiMiioMapper {
  public static toSnapshot(configArg: IXiaomiMiioConfig, connectedArg?: boolean, eventsArg: IXiaomiMiioEvent[] = []): IXiaomiMiioSnapshot {
    const source = configArg.snapshot;
    const primaryDevice = this.primaryDevice(configArg, source);
    const devices = this.uniqueDevices([
      ...(source?.devices || []),
      ...(source?.device ? [source.device] : []),
      ...(configArg.devices || []),
      ...(primaryDevice ? [primaryDevice] : []),
    ]);
    const connected = connectedArg ?? source?.connected ?? Boolean(source || configArg.devices?.length || configArg.state || configArg.properties?.length);
    return {
      connected,
      configured: Boolean(configArg.host && configArg.token),
      host: configArg.host || source?.host,
      port: configArg.port || source?.port || defaultMiioPort,
      tokenConfigured: Boolean(configArg.token || source?.tokenConfigured),
      device: primaryDevice || source?.device || devices[0],
      devices,
      entities: [...(source?.entities || []), ...(configArg.entities || [])],
      events: [...(source?.events || []), ...(configArg.events || []), ...eventsArg],
      transport: {
        protocol: source?.transport?.protocol || (source ? 'snapshot' : 'manual'),
        host: configArg.host || source?.transport?.host || source?.host,
        port: configArg.port || source?.transport?.port || source?.port || defaultMiioPort,
        tokenConfigured: Boolean(configArg.token || source?.transport?.tokenConfigured || source?.tokenConfigured),
        encryptedUdpImplemented: false,
      },
      metadata: source?.metadata,
    };
  }

  public static toDevices(snapshotArg: IXiaomiMiioSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const devices = new Map<string, plugins.shxInterfaces.data.IDeviceDefinition>();
    for (const device of this.allDevices(snapshotArg)) {
      const updatedAt = device.updatedAt || new Date().toISOString();
      const deviceId = this.deviceId(device);
      const properties = this.propertiesForDevice(device);
      const features = this.uniqueFeatures(properties.map((propertyArg) => this.featureForProperty(propertyArg, device)));
      const state = properties.map((propertyArg) => ({
        featureId: this.slug(propertyArg.key),
        value: this.deviceStateValue(propertyArg.value ?? this.normalizedState(device)[propertyArg.key] ?? null),
        updatedAt,
      }));
      if (!features.length) {
        features.push({ id: 'availability', capability: 'sensor', name: 'Availability', readable: true, writable: false });
        state.push({ featureId: 'availability', value: device.available === false ? 'offline' : 'online', updatedAt });
      }
      devices.set(deviceId, {
        id: deviceId,
        integrationDomain: 'xiaomi_miio',
        name: this.deviceName(device),
        protocol: 'unknown',
        manufacturer: device.manufacturer || 'Xiaomi',
        model: device.model,
        online: device.available !== false && device.online !== false && (snapshotArg.connected || Boolean(properties.length || device.host)),
        features,
        state,
        metadata: {
          miioProtocol: 'miio-udp',
          host: device.host || snapshotArg.host,
          port: device.port || snapshotArg.port || defaultMiioPort,
          macAddress: device.macAddress,
          did: device.did,
          firmwareVersion: device.firmwareVersion,
          hardwareVersion: device.hardwareVersion,
          kind: this.inferDeviceKind(device),
          tokenConfigured: Boolean(device.tokenConfigured || snapshotArg.tokenConfigured),
          encryptedUdpImplemented: false,
          ...device.metadata,
        },
      });
    }

    for (const entity of this.allEntityDescriptors(snapshotArg)) {
      const deviceId = this.entityDeviceId(snapshotArg, entity);
      const device = devices.get(deviceId);
      if (!device) {
        continue;
      }
      const featureId = this.slug(entity.propertyKey || entity.property_key || entity.uniqueId || entity.unique_id || entity.id || entity.entityId || entity.name || 'entity');
      if (!device.features.some((featureArg) => featureArg.id === featureId)) {
        device.features.push({
          id: featureId,
          capability: this.capabilityForPlatform(this.corePlatform(entity.platform || entity.kind || 'sensor')),
          name: entity.name || entity.entityId || entity.id || 'Xiaomi Miio entity',
          readable: true,
          writable: entity.writable === true,
          unit: entity.unit,
        });
        device.state.push({ featureId, value: this.deviceStateValue(entity.state ?? entity.value ?? null), updatedAt: new Date().toISOString() });
      }
    }

    return [...devices.values()];
  }

  public static toEntities(snapshotArg: IXiaomiMiioSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    const seen = new Set<string>();
    const addEntity = (entityArg: IIntegrationEntity | undefined) => {
      if (!entityArg || seen.has(entityArg.id)) {
        return;
      }
      seen.add(entityArg.id);
      entities.push(entityArg);
    };

    for (const descriptor of this.allEntityDescriptors(snapshotArg)) {
      addEntity(this.entityFromDescriptor(snapshotArg, descriptor));
    }

    for (const device of this.allDevices(snapshotArg)) {
      addEntity(this.primaryEntityForDevice(device));
      for (const property of this.propertiesForDevice(device)) {
        addEntity(this.entityForProperty(device, property));
      }
    }

    return entities;
  }

  public static commandForService(snapshotArg: IXiaomiMiioSnapshot, requestArg: IServiceCallRequest): IXiaomiMiioClientCommand | undefined {
    const targetEntity = this.findTargetEntity(snapshotArg, requestArg);
    const targetDevice = this.findTargetDevice(snapshotArg, requestArg, targetEntity);
    const kind = this.kindForCommand(requestArg, targetEntity, targetDevice);
    if (['start', 'stop', 'pause', 'return_home'].includes(requestArg.service) && (kind === 'vacuum' || requestArg.domain === 'vacuum')) {
      return this.command(requestArg, targetEntity, targetDevice, kind, requestArg.service, [], { action: requestArg.service });
    }
    if (requestArg.service === 'turn_on' || requestArg.service === 'turn_off') {
      const value = requestArg.service === 'turn_on';
      return this.command(requestArg, targetEntity, targetDevice, kind, requestArg.service, [], { power: value });
    }
    if (requestArg.service === 'set_value') {
      const propertyKey = this.stringValue(requestArg.data?.propertyKey || requestArg.data?.property_key || requestArg.data?.property || targetEntity?.attributes?.propertyKey);
      if (requestArg.data?.value === undefined) {
        return undefined;
      }
      return this.command(requestArg, targetEntity, targetDevice, kind, 'set_value', [requestArg.data.value], { propertyKey, value: requestArg.data.value });
    }
    return undefined;
  }

  private static primaryDevice(configArg: IXiaomiMiioConfig, sourceArg?: IXiaomiMiioSnapshot): IXiaomiMiioDeviceState | undefined {
    if (!configArg.host && !configArg.model && !configArg.name && !configArg.state && !configArg.properties?.length && !configArg.device) {
      return undefined;
    }
    const base = configArg.device || sourceArg?.device || {};
    return {
      ...base,
      id: base.id || configArg.deviceId || configArg.macAddress || configArg.host || configArg.model || 'configured',
      host: configArg.host || base.host,
      port: configArg.port || base.port || defaultMiioPort,
      model: configArg.model || base.model,
      name: configArg.name || base.name || configArg.model || 'Xiaomi Miio device',
      macAddress: configArg.macAddress || base.macAddress,
      kind: configArg.kind || base.kind,
      tokenConfigured: Boolean(configArg.token || base.tokenConfigured),
      state: { ...this.asRecord(base.state), ...this.asRecord(configArg.state) },
      properties: [...(base.properties || []), ...(configArg.properties || [])],
      entities: [...(base.entities || []), ...(configArg.entities || [])],
    };
  }

  private static allDevices(snapshotArg: IXiaomiMiioSnapshot): IXiaomiMiioDeviceState[] {
    const flattened: IXiaomiMiioDeviceState[] = [];
    const visit = (deviceArg: IXiaomiMiioDeviceState) => {
      flattened.push(deviceArg);
      for (const child of deviceArg.children || []) {
        visit(child);
      }
    };
    for (const device of snapshotArg.devices.length ? snapshotArg.devices : snapshotArg.device ? [snapshotArg.device] : []) {
      visit(device);
    }
    return this.uniqueDevices(flattened);
  }

  private static allEntityDescriptors(snapshotArg: IXiaomiMiioSnapshot): IXiaomiMiioEntityDescriptor[] {
    const entities = [...snapshotArg.entities];
    for (const device of this.allDevices(snapshotArg)) {
      for (const entity of device.entities || []) {
        entities.push({ ...entity, deviceId: entity.deviceId || entity.device_id || this.deviceId(device), kind: entity.kind || this.inferDeviceKind(device) });
      }
    }
    return entities;
  }

  private static uniqueDevices(devicesArg: IXiaomiMiioDeviceState[]): IXiaomiMiioDeviceState[] {
    const devices = new Map<string, IXiaomiMiioDeviceState>();
    for (const device of devicesArg) {
      devices.set(this.rawDeviceKey(device), { ...devices.get(this.rawDeviceKey(device)), ...device });
    }
    return [...devices.values()];
  }

  private static uniqueFeatures(featuresArg: plugins.shxInterfaces.data.IDeviceFeature[]): plugins.shxInterfaces.data.IDeviceFeature[] {
    const features = new Map<string, plugins.shxInterfaces.data.IDeviceFeature>();
    for (const feature of featuresArg) {
      features.set(feature.id, { ...features.get(feature.id), ...feature });
    }
    return [...features.values()];
  }

  private static propertiesForDevice(deviceArg: IXiaomiMiioDeviceState): IXiaomiMiioProperty[] {
    const state = this.normalizedState(deviceArg);
    const properties = [...(deviceArg.properties || [])];
    const existing = new Set(properties.map((propertyArg) => propertyArg.key));
    const kind = this.inferDeviceKind(deviceArg);
    for (const [key, value] of Object.entries(state)) {
      if (existing.has(key) || value === undefined || this.isRecord(value) && key !== 'error') {
        continue;
      }
      properties.push({
        key,
        value,
        name: this.title(key),
        unit: sensorUnits[key],
        readable: true,
        writable: writableKeys.has(key),
        platform: this.platformForProperty(key, value, kind),
      });
    }
    return properties;
  }

  private static normalizedState(deviceArg: IXiaomiMiioDeviceState): IXiaomiMiioStateRecord {
    const state: IXiaomiMiioStateRecord = { ...this.asRecord(deviceArg.state) };
    for (const key of topLevelStateKeys) {
      if (deviceArg[key] !== undefined && state[key] === undefined) {
        state[key] = deviceArg[key];
      }
    }
    if (this.isRecord(state.status)) {
      for (const [key, value] of Object.entries(state.status)) {
        if (state[key] === undefined) {
          state[key] = value;
        }
      }
    }
    if (state.is_on === undefined && typeof state.power === 'string') {
      state.is_on = ['on', 'true', '1'].includes(state.power.toLowerCase());
    }
    if (state.battery === undefined && state.battery_level !== undefined) {
      state.battery = state.battery_level;
    }
    return state;
  }

  private static primaryEntityForDevice(deviceArg: IXiaomiMiioDeviceState): IIntegrationEntity | undefined {
    const kind = this.inferDeviceKind(deviceArg);
    const state = this.normalizedState(deviceArg);
    const domain = this.entityDomainForKind(kind);
    if (!domain) {
      return undefined;
    }
    const name = this.deviceName(deviceArg);
    const deviceId = this.deviceId(deviceArg);
    return {
      id: `${domain}.${this.slug(name)}`,
      uniqueId: `xiaomi_miio_${this.slug(`${this.rawDeviceKey(deviceArg)}_${kind}`)}`,
      integrationDomain: 'xiaomi_miio',
      deviceId,
      platform: this.corePlatform(kind === 'humidifier' ? 'climate' : kind === 'vacuum' ? 'sensor' : domain),
      name,
      state: this.primaryEntityState(kind, state),
      attributes: {
        xiaomiMiioKind: kind,
        xiaomiMiioPlatform: domain,
        model: deviceArg.model,
        battery: state.battery,
        brightness: state.brightness,
        colorTemperature: state.color_temperature,
        percentage: state.percentage ?? state.speed ?? state.fan_level,
        presetMode: state.preset_mode ?? state.mode,
        oscillating: state.oscillating ?? state.oscillate,
        currentHumidity: state.current_humidity ?? state.humidity,
        targetHumidity: state.target_humidity,
        currentPosition: state.current_position ?? state.position,
        fanSpeed: state.fan_speed ?? state.fanspeed,
        error: state.error,
      },
      available: deviceArg.available !== false && deviceArg.online !== false,
    };
  }

  private static entityForProperty(deviceArg: IXiaomiMiioDeviceState, propertyArg: IXiaomiMiioProperty): IIntegrationEntity | undefined {
    const kind = this.inferDeviceKind(deviceArg);
    const key = propertyArg.key;
    const platform = this.corePlatform(propertyArg.platform || this.platformForProperty(key, propertyArg.value, kind));
    if (primaryControlKeys.has(key) && platform !== 'number') {
      return undefined;
    }
    if (!sensorKeys.has(key) && !propertyArg.unit && propertyArg.writable !== true && platform === 'sensor') {
      return undefined;
    }
    const name = `${this.deviceName(deviceArg)} ${propertyArg.name || this.title(key)}`;
    return {
      id: `${platform}.${this.slug(name)}`,
      uniqueId: `xiaomi_miio_${this.slug(`${this.rawDeviceKey(deviceArg)}_${key}`)}`,
      integrationDomain: 'xiaomi_miio',
      deviceId: this.deviceId(deviceArg),
      platform,
      name,
      state: this.entityStateValue(propertyArg.value),
      attributes: {
        xiaomiMiioKind: kind,
        propertyKey: key,
        deviceClass: propertyArg.deviceClass,
        unit: propertyArg.unit,
        writable: propertyArg.writable === true,
      },
      available: deviceArg.available !== false && deviceArg.online !== false,
    };
  }

  private static entityFromDescriptor(snapshotArg: IXiaomiMiioSnapshot, entityArg: IXiaomiMiioEntityDescriptor): IIntegrationEntity {
    const platform = this.corePlatform(entityArg.platform || entityArg.kind || 'sensor');
    const domain = this.entityDomainForExplicit(entityArg.platform || entityArg.kind || platform);
    const name = entityArg.name || entityArg.entityId || entityArg.id || 'Xiaomi Miio entity';
    return {
      id: entityArg.entityId || entityArg.id || `${domain}.${this.slug(name)}`,
      uniqueId: entityArg.uniqueId || entityArg.unique_id || `xiaomi_miio_${this.slug(entityArg.id || entityArg.entityId || name)}`,
      integrationDomain: 'xiaomi_miio',
      deviceId: this.entityDeviceId(snapshotArg, entityArg),
      platform,
      name,
      state: this.entityStateValue(entityArg.state ?? entityArg.value),
      attributes: {
        ...entityArg.attributes,
        xiaomiMiioKind: entityArg.kind,
        xiaomiMiioPlatform: entityArg.platform,
        propertyKey: entityArg.propertyKey || entityArg.property_key,
        deviceClass: entityArg.deviceClass || entityArg.device_class,
        unit: entityArg.unit,
        writable: entityArg.writable === true,
      },
      available: entityArg.available !== false,
    };
  }

  private static findTargetEntity(snapshotArg: IXiaomiMiioSnapshot, requestArg: IServiceCallRequest): IIntegrationEntity | undefined {
    const entities = this.toEntities(snapshotArg);
    if (requestArg.target.entityId) {
      return entities.find((entityArg) => entityArg.id === requestArg.target.entityId || entityArg.uniqueId === requestArg.target.entityId);
    }
    if (requestArg.target.deviceId) {
      return entities.find((entityArg) => entityArg.deviceId === requestArg.target.deviceId);
    }
    if (requestArg.domain === 'vacuum') {
      return entities.find((entityArg) => entityArg.id.startsWith('vacuum.'));
    }
    return entities.find((entityArg) => entityArg.id.startsWith(`${requestArg.domain}.`));
  }

  private static findTargetDevice(snapshotArg: IXiaomiMiioSnapshot, requestArg: IServiceCallRequest, entityArg?: IIntegrationEntity): IXiaomiMiioDeviceState | undefined {
    const devices = this.allDevices(snapshotArg);
    const deviceId = requestArg.target.deviceId || entityArg?.deviceId;
    if (deviceId) {
      return devices.find((deviceArg) => this.deviceId(deviceArg) === deviceId || this.rawDeviceKey(deviceArg) === deviceId);
    }
    if (requestArg.domain === 'vacuum') {
      return devices.find((deviceArg) => this.inferDeviceKind(deviceArg) === 'vacuum');
    }
    return devices[0];
  }

  private static kindForCommand(requestArg: IServiceCallRequest, entityArg?: IIntegrationEntity, deviceArg?: IXiaomiMiioDeviceState): TXiaomiMiioDeviceKind {
    const attrKind = this.stringValue(entityArg?.attributes?.xiaomiMiioKind);
    if (attrKind) {
      return attrKind;
    }
    if (deviceArg) {
      return this.inferDeviceKind(deviceArg);
    }
    if (requestArg.domain === 'vacuum') {
      return 'vacuum';
    }
    if (requestArg.domain === 'light' || requestArg.domain === 'switch' || requestArg.domain === 'fan' || requestArg.domain === 'cover') {
      return requestArg.domain;
    }
    if (requestArg.domain === 'humidifier' || requestArg.domain === 'climate') {
      return 'humidifier';
    }
    return 'unknown';
  }

  private static command(requestArg: IServiceCallRequest, entityArg: IIntegrationEntity | undefined, deviceArg: IXiaomiMiioDeviceState | undefined, kindArg: TXiaomiMiioDeviceKind, methodArg: string, paramsArg: unknown[], payloadArg: Record<string, unknown>): IXiaomiMiioClientCommand {
    return {
      type: 'service.command',
      service: requestArg.service,
      method: methodArg,
      params: paramsArg,
      platform: entityArg?.attributes?.xiaomiMiioPlatform as string | undefined || requestArg.domain,
      kind: kindArg,
      deviceId: entityArg?.deviceId || (deviceArg ? this.deviceId(deviceArg) : requestArg.target.deviceId),
      entityId: entityArg?.id || requestArg.target.entityId,
      uniqueId: entityArg?.uniqueId,
      propertyKey: this.stringValue(payloadArg.propertyKey || entityArg?.attributes?.propertyKey),
      value: payloadArg.value,
      target: requestArg.target,
      payload: { ...payloadArg, data: requestArg.data || {} },
    };
  }

  private static featureForProperty(propertyArg: IXiaomiMiioProperty, deviceArg: IXiaomiMiioDeviceState): plugins.shxInterfaces.data.IDeviceFeature {
    const kind = this.inferDeviceKind(deviceArg);
    return {
      id: this.slug(propertyArg.key),
      capability: this.capabilityForPlatform(propertyArg.platform || this.platformForProperty(propertyArg.key, propertyArg.value, kind)),
      name: propertyArg.name || this.title(propertyArg.key),
      readable: propertyArg.readable !== false,
      writable: propertyArg.writable === true || writableKeys.has(propertyArg.key),
      unit: propertyArg.unit || sensorUnits[propertyArg.key],
    };
  }

  private static platformForProperty(keyArg: string, valueArg: unknown, kindArg: TXiaomiMiioDeviceKind): TEntityPlatform {
    if (keyArg === 'brightness' || keyArg === 'color_temperature' || keyArg === 'rgb' || kindArg === 'light' && ['is_on', 'on', 'power'].includes(keyArg)) {
      return 'light';
    }
    if (keyArg === 'position' || keyArg === 'current_position' || keyArg === 'target_position' || kindArg === 'cover') {
      return 'cover';
    }
    if (typeof valueArg === 'number' && numberControlKeys.has(keyArg)) {
      return 'number';
    }
    if (kindArg === 'fan' && ['is_on', 'on', 'power', 'speed', 'percentage', 'fan_level', 'oscillate', 'oscillating'].includes(keyArg)) {
      return 'fan';
    }
    if (kindArg === 'humidifier' && ['is_on', 'on', 'power', 'target_humidity', 'mode'].includes(keyArg)) {
      return typeof valueArg === 'number' && keyArg === 'target_humidity' ? 'number' : 'climate';
    }
    if (typeof valueArg === 'number' && writableKeys.has(keyArg) && !sensorKeys.has(keyArg)) {
      return 'number';
    }
    if (typeof valueArg === 'boolean' && !['is_on', 'on', 'power'].includes(keyArg)) {
      return 'switch';
    }
    if (kindArg === 'switch' && ['is_on', 'on', 'power'].includes(keyArg)) {
      return 'switch';
    }
    return 'sensor';
  }

  private static inferDeviceKind(deviceArg: IXiaomiMiioDeviceState): TXiaomiMiioDeviceKind {
    const explicit = this.stringValue(deviceArg.kind || deviceArg.type || deviceArg.platform)?.toLowerCase();
    if (explicit && explicit !== 'device') {
      return explicit;
    }
    const model = (deviceArg.model || '').toLowerCase();
    const state = this.normalizedStateShallow(deviceArg);
    if (model.includes('vacuum') || model.startsWith('roborock.') || model.startsWith('rockrobo.') || state.state_code !== undefined && (state.clean_area !== undefined || state.battery !== undefined)) {
      return 'vacuum';
    }
    if (model.includes('humidifier') || model.includes('deerma.humidifier') || state.target_humidity !== undefined) {
      return 'humidifier';
    }
    if (model.includes('airpurifier') || model.includes('airfresh') || model.includes('.fan.') || state.fan_level !== undefined || state.oscillate !== undefined || state.motor_speed !== undefined) {
      return 'fan';
    }
    if (model.includes('light') || state.brightness !== undefined || state.color_temperature !== undefined || state.rgb !== undefined) {
      return 'light';
    }
    if (model.includes('plug') || model.includes('powerstrip') || model.includes('switch')) {
      return 'switch';
    }
    if (model.includes('curtain') || model.includes('cover') || state.position !== undefined || state.current_position !== undefined) {
      return 'cover';
    }
    if (model.includes('gateway')) {
      return 'gateway';
    }
    if (model.includes('airmonitor') || state.aqi !== undefined || state.pm25 !== undefined) {
      return 'sensor';
    }
    if (state.is_on !== undefined || state.power !== undefined) {
      return 'switch';
    }
    return 'sensor';
  }

  private static normalizedStateShallow(deviceArg: IXiaomiMiioDeviceState): IXiaomiMiioStateRecord {
    const state: IXiaomiMiioStateRecord = { ...this.asRecord(deviceArg.state) };
    for (const key of topLevelStateKeys) {
      if (deviceArg[key] !== undefined && state[key] === undefined) {
        state[key] = deviceArg[key];
      }
    }
    if (this.isRecord(state.status)) {
      for (const [key, value] of Object.entries(state.status)) {
        if (state[key] === undefined) {
          state[key] = value;
        }
      }
    }
    return state;
  }

  private static primaryEntityState(kindArg: TXiaomiMiioDeviceKind, stateArg: IXiaomiMiioStateRecord): unknown {
    if (kindArg === 'vacuum') {
      return this.vacuumActivity(stateArg);
    }
    if (kindArg === 'cover') {
      const position = this.numberValue(stateArg.current_position ?? stateArg.position);
      if (position !== undefined) {
        return position <= 0 ? 'closed' : 'open';
      }
      return stateArg.state ?? 'unknown';
    }
    if (kindArg === 'sensor') {
      return stateArg.aqi ?? stateArg.pm25 ?? stateArg.temperature ?? stateArg.humidity ?? 'unknown';
    }
    return this.powerState(stateArg);
  }

  private static vacuumActivity(stateArg: IXiaomiMiioStateRecord): string {
    const stateCode = this.numberValue(stateArg.state_code);
    if (stateCode !== undefined) {
      if ([5, 7, 11, 16, 17, 18].includes(stateCode)) {
        return 'cleaning';
      }
      if ([6, 15, 26].includes(stateCode)) {
        return 'returning';
      }
      if ([8, 22, 23, 100].includes(stateCode)) {
        return 'docked';
      }
      if (stateCode === 10) {
        return 'paused';
      }
      if ([9, 12, 101].includes(stateCode)) {
        return 'error';
      }
      return 'idle';
    }
    return this.stringValue(stateArg.state || stateArg.status) || 'unknown';
  }

  private static powerState(stateArg: IXiaomiMiioStateRecord): string {
    const value = stateArg.is_on ?? stateArg.on ?? stateArg.power;
    if (typeof value === 'boolean') {
      return value ? 'on' : 'off';
    }
    if (typeof value === 'string') {
      const normalized = value.toLowerCase();
      if (['on', 'true', '1'].includes(normalized)) {
        return 'on';
      }
      if (['off', 'false', '0'].includes(normalized)) {
        return 'off';
      }
      return normalized;
    }
    return 'unknown';
  }

  private static entityDeviceId(snapshotArg: IXiaomiMiioSnapshot, entityArg: IXiaomiMiioEntityDescriptor): string {
    const explicit = entityArg.deviceId || entityArg.device_id;
    if (explicit) {
      return explicit;
    }
    const device = this.allDevices(snapshotArg).find((deviceArg) => entityArg.deviceId === this.deviceId(deviceArg) || entityArg.device_id === this.deviceId(deviceArg));
    return device ? this.deviceId(device) : this.deviceId(snapshotArg.device || snapshotArg.devices[0] || { id: 'configured' });
  }

  private static deviceId(deviceArg: IXiaomiMiioDeviceState): string {
    return `xiaomi_miio.device.${this.slug(this.rawDeviceKey(deviceArg))}`;
  }

  private static rawDeviceKey(deviceArg: IXiaomiMiioDeviceState): string {
    return String(deviceArg.id || deviceArg.did || deviceArg.macAddress || deviceArg.host || deviceArg.model || deviceArg.name || 'configured');
  }

  private static deviceName(deviceArg: IXiaomiMiioDeviceState): string {
    return deviceArg.name || deviceArg.model || deviceArg.host || 'Xiaomi Miio device';
  }

  private static entityDomainForKind(kindArg: TXiaomiMiioDeviceKind): string | undefined {
    if (kindArg === 'vacuum') {
      return 'vacuum';
    }
    if (kindArg === 'humidifier') {
      return 'humidifier';
    }
    if (kindArg === 'light' || kindArg === 'switch' || kindArg === 'fan' || kindArg === 'cover') {
      return kindArg;
    }
    if (kindArg === 'sensor' || kindArg === 'gateway') {
      return 'sensor';
    }
    return undefined;
  }

  private static entityDomainForExplicit(platformArg: unknown): string {
    const platform = String(platformArg || 'sensor');
    if (platform === 'vacuum' || platform === 'humidifier') {
      return platform;
    }
    return this.corePlatform(platform);
  }

  private static corePlatform(platformArg: unknown): TEntityPlatform {
    const platform = String(platformArg || 'sensor').toLowerCase();
    if (platform === 'vacuum') {
      return 'sensor';
    }
    if (platform === 'humidifier') {
      return 'climate';
    }
    const allowed: TEntityPlatform[] = ['light', 'switch', 'sensor', 'binary_sensor', 'button', 'climate', 'cover', 'fan', 'media_player', 'number', 'select', 'text', 'update'];
    return allowed.includes(platform as TEntityPlatform) ? platform as TEntityPlatform : 'sensor';
  }

  private static capabilityForPlatform(platformArg: unknown): plugins.shxInterfaces.data.TDeviceCapability {
    const platform = String(platformArg || 'sensor').toLowerCase();
    if (platform === 'light') {
      return 'light';
    }
    if (platform === 'cover') {
      return 'cover';
    }
    if (platform === 'fan' || platform === 'vacuum') {
      return 'fan';
    }
    if (platform === 'climate' || platform === 'humidifier') {
      return 'climate';
    }
    return platform === 'switch' || platform === 'number' || platform === 'select' || platform === 'button' ? 'switch' : 'sensor';
  }

  private static entityStateValue(valueArg: unknown): unknown {
    if (valueArg === undefined) {
      return 'unknown';
    }
    if (typeof valueArg === 'boolean') {
      return valueArg ? 'on' : 'off';
    }
    return valueArg;
  }

  private static deviceStateValue(valueArg: unknown): plugins.shxInterfaces.data.TDeviceStateValue {
    if (typeof valueArg === 'string' || typeof valueArg === 'number' || typeof valueArg === 'boolean' || valueArg === null || this.isRecord(valueArg)) {
      return valueArg;
    }
    return Array.isArray(valueArg) ? JSON.stringify(valueArg) : String(valueArg);
  }

  private static stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg ? valueArg : undefined;
  }

  private static numberValue(valueArg: unknown): number | undefined {
    return typeof valueArg === 'number' && Number.isFinite(valueArg) ? valueArg : undefined;
  }

  private static asRecord(valueArg: unknown): Record<string, unknown> {
    return this.isRecord(valueArg) ? valueArg : {};
  }

  private static isRecord(valueArg: unknown): valueArg is Record<string, unknown> {
    return typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg);
  }

  private static title(valueArg: string): string {
    return valueArg.replace(/[_-]+/g, ' ').replace(/\b\w/g, (matchArg) => matchArg.toUpperCase());
  }

  private static slug(valueArg: string): string {
    return String(valueArg).toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'xiaomi_miio';
  }
}
