import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IDenonReceiverInfo, IDenonSnapshot } from './denon.types.js';
import { denonDefaultName, denonDomain } from './denon.types.js';

export class DenonMapper {
  public static toDevices(snapshotArg: IDenonSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.lastUpdated || new Date().toISOString();
    return [{
      id: this.deviceId(snapshotArg),
      integrationDomain: denonDomain,
      name: this.receiverName(snapshotArg.receiverInfo),
      protocol: 'unknown',
      manufacturer: snapshotArg.receiverInfo.manufacturer || 'Denon',
      model: snapshotArg.receiverInfo.modelName,
      online: snapshotArg.online && snapshotArg.available,
      features: [
        { id: 'power', capability: 'media', name: 'Power', readable: true, writable: true },
        { id: 'source', capability: 'media', name: 'Source', readable: true, writable: true },
        { id: 'volume', capability: 'media', name: 'Volume', readable: true, writable: true, unit: '%' },
        { id: 'muted', capability: 'media', name: 'Muted', readable: true, writable: true },
        { id: 'media_title', capability: 'media', name: 'Media title', readable: true, writable: false },
      ],
      state: [
        { featureId: 'power', value: this.powerState(snapshotArg), updatedAt },
        { featureId: 'source', value: snapshotArg.state.sourceName || snapshotArg.state.source || null, updatedAt },
        { featureId: 'volume', value: typeof snapshotArg.state.volumeLevel === 'number' ? Math.round(snapshotArg.state.volumeLevel * 100) : null, updatedAt },
        { featureId: 'muted', value: snapshotArg.state.muted ?? null, updatedAt },
        { featureId: 'media_title', value: snapshotArg.state.mediaTitle || null, updatedAt },
      ],
      metadata: this.cleanAttributes({
        host: snapshotArg.receiverInfo.host,
        port: snapshotArg.receiverInfo.port,
        protocol: 'telnet',
        serialNumber: snapshotArg.receiverInfo.serialNumber,
        source: snapshotArg.source,
        error: snapshotArg.error,
      }),
    }];
  }

  public static toEntities(snapshotArg: IDenonSnapshot): IIntegrationEntity[] {
    const entityBase = this.entityBase(snapshotArg);
    const deviceId = this.deviceId(snapshotArg);
    const entities: IIntegrationEntity[] = [{
      id: `media_player.${entityBase}`,
      uniqueId: `denon_${this.uniqueBase(snapshotArg)}_media_player`,
      integrationDomain: denonDomain,
      deviceId,
      platform: 'media_player',
      name: this.receiverName(snapshotArg.receiverInfo),
      state: this.mediaState(snapshotArg),
      attributes: this.cleanAttributes({
        deviceClass: 'receiver',
        power: snapshotArg.state.power,
        volumeLevel: snapshotArg.state.volumeLevel,
        volume: snapshotArg.state.volume,
        volumeMax: snapshotArg.state.volumeMax,
        isVolumeMuted: snapshotArg.state.muted,
        source: snapshotArg.state.sourceName || snapshotArg.state.source,
        sourceCode: snapshotArg.state.source,
        sourceList: snapshotArg.state.sourceList,
        mediaTitle: snapshotArg.state.mediaTitle,
        mediaContentType: snapshotArg.state.mediaMode ? 'music' : undefined,
        supportedFeatures: snapshotArg.state.supportedFeatures,
        error: snapshotArg.error,
      }),
      available: snapshotArg.online && snapshotArg.available,
    }, {
      id: `sensor.${entityBase}_source`,
      uniqueId: `denon_${this.uniqueBase(snapshotArg)}_source`,
      integrationDomain: denonDomain,
      deviceId,
      platform: 'sensor',
      name: `${this.receiverName(snapshotArg.receiverInfo)} Source`,
      state: snapshotArg.state.sourceName || snapshotArg.state.source || 'unknown',
      attributes: this.cleanAttributes({ sourceCode: snapshotArg.state.source }),
      available: snapshotArg.online && snapshotArg.available,
    }];

    if (typeof snapshotArg.state.muted === 'boolean') {
      entities.push({
        id: `switch.${entityBase}_mute`,
        uniqueId: `denon_${this.uniqueBase(snapshotArg)}_mute`,
        integrationDomain: denonDomain,
        deviceId,
        platform: 'switch',
        name: `${this.receiverName(snapshotArg.receiverInfo)} Mute`,
        state: snapshotArg.state.muted,
        attributes: {},
        available: snapshotArg.online && snapshotArg.available,
      });
    }

    return entities;
  }

  public static deviceId(snapshotArg: IDenonSnapshot): string {
    return `denon.receiver.${this.uniqueBase(snapshotArg)}`;
  }

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

  private static mediaState(snapshotArg: IDenonSnapshot): string {
    if (!snapshotArg.online || !snapshotArg.available) {
      return 'unavailable';
    }
    if (this.powerState(snapshotArg) === 'off') {
      return 'off';
    }
    const state = snapshotArg.state.state?.toLowerCase();
    if (state === 'playing' || state === 'paused' || state === 'idle') {
      return state;
    }
    return 'on';
  }

  private static powerState(snapshotArg: IDenonSnapshot): string {
    if (!snapshotArg.online || !snapshotArg.available) {
      return 'unavailable';
    }
    return snapshotArg.state.power === 'PWSTANDBY' || snapshotArg.state.state === 'off' ? 'off' : 'on';
  }

  private static deviceIdentity(snapshotArg: IDenonSnapshot): string {
    return snapshotArg.receiverInfo.serialNumber || snapshotArg.receiverInfo.host || this.receiverName(snapshotArg.receiverInfo);
  }

  private static entityBase(snapshotArg: IDenonSnapshot): string {
    return this.slug(this.receiverName(snapshotArg.receiverInfo));
  }

  private static uniqueBase(snapshotArg: IDenonSnapshot): string {
    return this.slug(this.deviceIdentity(snapshotArg));
  }

  private static receiverName(infoArg: IDenonReceiverInfo): string {
    return infoArg.name || infoArg.modelName || denonDefaultName;
  }

  private static cleanAttributes(attributesArg: Record<string, unknown>): Record<string, unknown> {
    return Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  }
}
