import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IShellyDeviceConfig, IShellyDeviceInfo, IShellySnapshot, IShellyStatus, IShellySwitchStatus } from './shelly.types.js';

export class ShellyMapper {
  public static toDevices(snapshotArg: IShellySnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const deviceId = this.deviceId(snapshotArg.deviceInfo, snapshotArg.status);
    const updatedAt = new Date().toISOString();
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'connectivity', value: 'online', updatedAt },
    ];

    if (snapshotArg.deviceInfo.ver || snapshotArg.deviceInfo.fw_id) {
      features.push({ id: 'firmware', capability: 'sensor', name: 'Firmware', readable: true, writable: false });
      state.push({ featureId: 'firmware', value: snapshotArg.deviceInfo.ver || snapshotArg.deviceInfo.fw_id || null, updatedAt });
    }

    for (const switchComponent of this.switchComponents(snapshotArg.status)) {
      const prefix = `switch_${switchComponent.id}`;
      const name = this.componentName(snapshotArg.deviceConfig, `switch:${switchComponent.id}`, `Switch ${switchComponent.id}`);
      features.push({ id: `${prefix}_output`, capability: 'switch', name, readable: true, writable: true });
      state.push({ featureId: `${prefix}_output`, value: switchComponent.status.output ?? null, updatedAt });
      this.pushNumericFeature(features, state, `${prefix}_power`, `${name} power`, switchComponent.status.apower, 'W', updatedAt);
      this.pushNumericFeature(features, state, `${prefix}_voltage`, `${name} voltage`, switchComponent.status.voltage, 'V', updatedAt);
      this.pushNumericFeature(features, state, `${prefix}_current`, `${name} current`, switchComponent.status.current, 'A', updatedAt);
      this.pushNumericFeature(features, state, `${prefix}_energy`, `${name} energy`, switchComponent.status.aenergy?.total, 'Wh', updatedAt);
      this.pushNumericFeature(features, state, `${prefix}_temperature`, `${name} temperature`, switchComponent.status.temperature?.tC, 'C', updatedAt);
    }

    return [{
      id: deviceId,
      integrationDomain: 'shelly',
      name: this.deviceName(snapshotArg.deviceInfo, snapshotArg.deviceConfig),
      protocol: 'http',
      manufacturer: 'Shelly',
      model: snapshotArg.deviceInfo.model || snapshotArg.deviceInfo.app,
      online: true,
      features,
      state,
      metadata: {
        generation: snapshotArg.deviceInfo.gen,
        firmwareId: snapshotArg.deviceInfo.fw_id,
        authEnabled: snapshotArg.deviceInfo.auth_en ?? snapshotArg.deviceInfo.auth,
        macAddress: snapshotArg.deviceInfo.mac || snapshotArg.status.sys?.mac,
        profile: snapshotArg.deviceInfo.profile,
      },
    }];
  }

  public static toEntities(snapshotArg: IShellySnapshot): IIntegrationEntity[] {
    const deviceId = this.deviceId(snapshotArg.deviceInfo, snapshotArg.status);
    const deviceSlug = this.slug(this.deviceName(snapshotArg.deviceInfo, snapshotArg.deviceConfig));
    const entities: IIntegrationEntity[] = [];

    for (const switchComponent of this.switchComponents(snapshotArg.status)) {
      const switchName = this.componentName(snapshotArg.deviceConfig, `switch:${switchComponent.id}`, `Switch ${switchComponent.id}`);
      const uniquePrefix = `shelly_${this.slug(deviceId)}_switch_${switchComponent.id}`;
      entities.push({
        id: `switch.${deviceSlug}_${switchComponent.id}`,
        uniqueId: uniquePrefix,
        integrationDomain: 'shelly',
        deviceId,
        platform: 'switch',
        name: switchName,
        state: switchComponent.status.output ? 'on' : 'off',
        attributes: {
          source: switchComponent.status.source,
          errors: switchComponent.status.errors,
        },
        available: true,
      });
      this.pushSensorEntity(entities, deviceId, `${uniquePrefix}_power`, `sensor.${deviceSlug}_${switchComponent.id}_power`, `${switchName} power`, switchComponent.status.apower, 'W');
      this.pushSensorEntity(entities, deviceId, `${uniquePrefix}_voltage`, `sensor.${deviceSlug}_${switchComponent.id}_voltage`, `${switchName} voltage`, switchComponent.status.voltage, 'V');
      this.pushSensorEntity(entities, deviceId, `${uniquePrefix}_current`, `sensor.${deviceSlug}_${switchComponent.id}_current`, `${switchName} current`, switchComponent.status.current, 'A');
      this.pushSensorEntity(entities, deviceId, `${uniquePrefix}_energy`, `sensor.${deviceSlug}_${switchComponent.id}_energy`, `${switchName} energy`, switchComponent.status.aenergy?.total, 'Wh');
      this.pushSensorEntity(entities, deviceId, `${uniquePrefix}_temperature`, `sensor.${deviceSlug}_${switchComponent.id}_temperature`, `${switchName} temperature`, switchComponent.status.temperature?.tC, 'C');
    }

    return entities;
  }

  private static pushNumericFeature(
    featuresArg: plugins.shxInterfaces.data.IDeviceFeature[],
    stateArg: plugins.shxInterfaces.data.IDeviceState[],
    idArg: string,
    nameArg: string,
    valueArg: number | null | undefined,
    unitArg: string,
    updatedAtArg: string
  ): void {
    if (typeof valueArg !== 'number') {
      return;
    }
    featuresArg.push({ id: idArg, capability: 'sensor', name: nameArg, readable: true, writable: false, unit: unitArg });
    stateArg.push({ featureId: idArg, value: valueArg, updatedAt: updatedAtArg });
  }

  private static pushSensorEntity(
    entitiesArg: IIntegrationEntity[],
    deviceIdArg: string,
    uniqueIdArg: string,
    idArg: string,
    nameArg: string,
    valueArg: number | null | undefined,
    unitArg: string
  ): void {
    if (typeof valueArg !== 'number') {
      return;
    }
    entitiesArg.push({
      id: idArg,
      uniqueId: uniqueIdArg,
      integrationDomain: 'shelly',
      deviceId: deviceIdArg,
      platform: 'sensor',
      name: nameArg,
      state: valueArg,
      attributes: { unit: unitArg },
      available: true,
    });
  }

  private static switchComponents(statusArg: IShellyStatus): Array<{ id: number; key: string; status: IShellySwitchStatus }> {
    const components: Array<{ id: number; key: string; status: IShellySwitchStatus }> = [];
    for (const [key, value] of Object.entries(statusArg)) {
      const match = /^switch:(\d+)$/.exec(key);
      if (!match || !this.isRecord(value)) {
        continue;
      }
      components.push({
        id: Number(match[1]),
        key,
        status: value as IShellySwitchStatus,
      });
    }
    return components.sort((leftArg, rightArg) => leftArg.id - rightArg.id);
  }

  private static deviceId(deviceInfoArg: IShellyDeviceInfo, statusArg: IShellyStatus): string {
    return `shelly.device.${this.slug(deviceInfoArg.id || deviceInfoArg.mac || statusArg.sys?.mac || 'unknown')}`;
  }

  private static deviceName(deviceInfoArg: IShellyDeviceInfo, configArg?: IShellyDeviceConfig): string {
    return configArg?.sys?.device?.name || deviceInfoArg.id || deviceInfoArg.model || 'Shelly Device';
  }

  private static componentName(configArg: IShellyDeviceConfig | undefined, keyArg: string, fallbackArg: string): string {
    const componentConfig = configArg?.[keyArg];
    if (this.isRecord(componentConfig) && typeof componentConfig.name === 'string' && componentConfig.name) {
      return componentConfig.name;
    }
    return fallbackArg;
  }

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

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