import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IDenonavrReceiverInfo, IDenonavrSnapshot, IDenonavrZoneState } from './denonavr.types.js';

export class DenonavrMapper {
  public static toDevices(snapshotArg: IDenonavrSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = new Date().toISOString();
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [];

    for (const zone of snapshotArg.zones) {
      const zoneSlug = this.slug(zone.zone);
      features.push(
        { id: `${zoneSlug}_power`, capability: 'media', name: `${this.zoneName(zone)} power`, readable: true, writable: true },
        { id: `${zoneSlug}_source`, capability: 'media', name: `${this.zoneName(zone)} source`, readable: true, writable: true },
        { id: `${zoneSlug}_volume`, capability: 'media', name: `${this.zoneName(zone)} volume`, readable: true, writable: true, unit: '%' },
        { id: `${zoneSlug}_muted`, capability: 'media', name: `${this.zoneName(zone)} muted`, readable: true, writable: true },
      );
      state.push(
        { featureId: `${zoneSlug}_power`, value: this.powerState(zone), updatedAt },
        { featureId: `${zoneSlug}_source`, value: zone.source || null, updatedAt },
        { featureId: `${zoneSlug}_volume`, value: typeof this.volumeLevel(zone) === 'number' ? Math.round((this.volumeLevel(zone) || 0) * 100) : null, updatedAt },
        { featureId: `${zoneSlug}_muted`, value: zone.muted ?? null, updatedAt },
      );
      if (zone.soundMode || zone.soundModeRaw) {
        features.push({ id: `${zoneSlug}_sound_mode`, capability: 'media', name: `${this.zoneName(zone)} sound mode`, readable: true, writable: true });
        state.push({ featureId: `${zoneSlug}_sound_mode`, value: zone.soundMode || zone.soundModeRaw || null, updatedAt });
      }
    }

    return [{
      id: this.deviceId(snapshotArg),
      integrationDomain: 'denonavr',
      name: this.receiverName(snapshotArg.receiverInfo),
      protocol: 'http',
      manufacturer: snapshotArg.receiverInfo.manufacturer || 'Denon',
      model: snapshotArg.receiverInfo.modelName || snapshotArg.receiverInfo.modelNumber,
      online: snapshotArg.zones.some((zoneArg) => zoneArg.available !== false),
      features,
      state,
      metadata: {
        host: snapshotArg.receiverInfo.host,
        port: snapshotArg.receiverInfo.port,
        serialNumber: snapshotArg.receiverInfo.serialNumber,
        receiverType: snapshotArg.receiverInfo.receiverType,
        softwareVersion: snapshotArg.receiverInfo.softwareVersion,
      },
    }];
  }

  public static toEntities(snapshotArg: IDenonavrSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    for (const zone of snapshotArg.zones) {
      const zoneEntityBase = this.entityBase(snapshotArg, zone);
      entities.push({
        id: `media_player.${zoneEntityBase}`,
        uniqueId: `denonavr_${this.uniqueBase(snapshotArg)}_${this.slug(zone.zone)}`,
        integrationDomain: 'denonavr',
        deviceId: this.deviceId(snapshotArg),
        platform: 'media_player',
        name: this.mediaEntityName(snapshotArg.receiverInfo, zone),
        state: this.mediaState(zone),
        attributes: {
          deviceClass: 'receiver',
          zone: zone.zone,
          power: zone.power,
          volumeLevel: this.volumeLevel(zone),
          volumeDb: zone.volumeDb,
          isVolumeMuted: zone.muted,
          source: zone.source,
          sourceList: zone.sourceList,
          soundMode: zone.soundMode,
          soundModeRaw: zone.soundModeRaw,
          soundModeList: zone.soundModeList,
          mediaTitle: zone.media?.title || (!zone.media ? zone.source : undefined),
          mediaArtist: zone.media?.artist || zone.media?.band,
          mediaAlbumName: zone.media?.album || zone.media?.station,
          mediaImageUrl: zone.media?.imageUrl,
          mediaContentType: zone.media?.contentType,
          dynamicEq: zone.dynamicEq,
          ecoMode: zone.ecoMode,
        },
        available: zone.available !== false,
      });

      entities.push({
        id: `sensor.${zoneEntityBase}_source`,
        uniqueId: `denonavr_${this.uniqueBase(snapshotArg)}_${this.slug(zone.zone)}_source`,
        integrationDomain: 'denonavr',
        deviceId: this.deviceId(snapshotArg),
        platform: 'sensor',
        name: `${this.mediaEntityName(snapshotArg.receiverInfo, zone)} Source`,
        state: zone.source || 'unknown',
        attributes: { zone: zone.zone, soundMode: zone.soundMode || zone.soundModeRaw, ecoMode: zone.ecoMode },
        available: zone.available !== false,
      });

      if (typeof zone.muted === 'boolean') {
        entities.push({
          id: `switch.${zoneEntityBase}_mute`,
          uniqueId: `denonavr_${this.uniqueBase(snapshotArg)}_${this.slug(zone.zone)}_mute`,
          integrationDomain: 'denonavr',
          deviceId: this.deviceId(snapshotArg),
          platform: 'switch',
          name: `${this.mediaEntityName(snapshotArg.receiverInfo, zone)} Mute`,
          state: zone.muted,
          attributes: { zone: zone.zone },
          available: zone.available !== false,
        });
      }

      if (typeof zone.dynamicEq === 'boolean') {
        entities.push({
          id: `switch.${zoneEntityBase}_dynamic_eq`,
          uniqueId: `denonavr_${this.uniqueBase(snapshotArg)}_${this.slug(zone.zone)}_dynamic_eq`,
          integrationDomain: 'denonavr',
          deviceId: this.deviceId(snapshotArg),
          platform: 'switch',
          name: `${this.mediaEntityName(snapshotArg.receiverInfo, zone)} Dynamic EQ`,
          state: zone.dynamicEq,
          attributes: { zone: zone.zone },
          available: zone.available !== false,
        });
      }
    }
    return entities;
  }

  private static mediaState(zoneArg: IDenonavrZoneState): string {
    if (this.powerState(zoneArg) === 'off') {
      return 'off';
    }
    const state = zoneArg.state?.toLowerCase();
    if (state === 'playing' || state === 'paused') {
      return state;
    }
    if (state === 'stopped') {
      return 'idle';
    }
    return 'on';
  }

  private static powerState(zoneArg: IDenonavrZoneState): string {
    const power = zoneArg.power?.toLowerCase();
    if (power === 'off' || power === 'standby') {
      return 'off';
    }
    if (!power && zoneArg.state?.toLowerCase() === 'off') {
      return 'off';
    }
    return 'on';
  }

  private static volumeLevel(zoneArg: IDenonavrZoneState): number | undefined {
    if (typeof zoneArg.volumeLevel === 'number') {
      return Math.max(0, Math.min(1, zoneArg.volumeLevel));
    }
    if (typeof zoneArg.volumeDb === 'number') {
      return Math.max(0, Math.min(1, (zoneArg.volumeDb + 80) / 100));
    }
    return undefined;
  }

  private static deviceId(snapshotArg: IDenonavrSnapshot): string {
    return `denonavr.receiver.${this.uniqueBase(snapshotArg)}`;
  }

  private static entityBase(snapshotArg: IDenonavrSnapshot, zoneArg: IDenonavrZoneState): string {
    const suffix = zoneArg.zone === 'Main' ? '' : `_${this.slug(zoneArg.zone)}`;
    return `${this.slug(this.receiverName(snapshotArg.receiverInfo))}${suffix}`;
  }

  private static uniqueBase(snapshotArg: IDenonavrSnapshot): string {
    return this.slug(snapshotArg.receiverInfo.serialNumber || snapshotArg.receiverInfo.modelName || snapshotArg.receiverInfo.host || this.receiverName(snapshotArg.receiverInfo));
  }

  private static receiverName(infoArg: IDenonavrReceiverInfo): string {
    return infoArg.name || infoArg.friendlyName || infoArg.modelName || 'Denon AVR';
  }

  private static mediaEntityName(infoArg: IDenonavrReceiverInfo, zoneArg: IDenonavrZoneState): string {
    const name = zoneArg.name || this.zoneName(zoneArg);
    return zoneArg.zone === 'Main' ? this.receiverName(infoArg) : `${this.receiverName(infoArg)} ${name}`;
  }

  private static zoneName(zoneArg: IDenonavrZoneState): string {
    return zoneArg.name || (zoneArg.zone === 'Main' ? 'Main Zone' : zoneArg.zone);
  }

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