import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IFrontierSiliconDeviceInfo, IFrontierSiliconPlayerState, IFrontierSiliconSnapshot } from './frontier_silicon.types.js';

export class FrontierSiliconMapper {
  public static toDevices(snapshotArg: IFrontierSiliconSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const player = snapshotArg.player;
    return [{
      id: this.deviceId(snapshotArg),
      integrationDomain: 'frontier_silicon',
      name: this.deviceName(snapshotArg.device),
      protocol: 'http',
      manufacturer: snapshotArg.device.manufacturer || 'Frontier Silicon',
      model: snapshotArg.device.model || 'FSAPI Radio',
      online: snapshotArg.online,
      features: [
        { id: 'power', capability: 'switch', name: 'Power', readable: true, writable: true },
        { id: 'playback', capability: 'media', name: 'Playback', 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: 'source', capability: 'media', name: 'Source', readable: true, writable: true },
        { id: 'sound_mode', capability: 'media', name: 'Sound mode', readable: true, writable: true },
        { id: 'presets', capability: 'media', name: 'Presets', readable: true, writable: true },
        { id: 'current_title', capability: 'media', name: 'Current title', readable: true, writable: false },
      ],
      state: [
        { featureId: 'power', value: player.power ?? null, updatedAt },
        { featureId: 'playback', value: this.mediaState(snapshotArg, player), updatedAt },
        { featureId: 'volume', value: this.volumePercent(player) ?? null, updatedAt },
        { featureId: 'muted', value: player.muted ?? null, updatedAt },
        { featureId: 'source', value: player.source || null, updatedAt },
        { featureId: 'sound_mode', value: player.soundMode?.label || null, updatedAt },
        { featureId: 'presets', value: snapshotArg.presets.length, updatedAt },
        { featureId: 'current_title', value: player.mediaTitle || null, updatedAt },
      ],
      metadata: {
        radioId: snapshotArg.device.radioId,
        host: snapshotArg.device.host,
        port: snapshotArg.device.port,
        deviceUrl: snapshotArg.device.deviceUrl,
        webfsapiUrl: snapshotArg.device.webfsapiUrl,
        version: snapshotArg.device.version,
        macAddress: snapshotArg.device.macAddress,
        rssi: snapshotArg.device.rssi,
        source: snapshotArg.source,
        error: snapshotArg.error,
      },
    }];
  }

  public static toEntities(snapshotArg: IFrontierSiliconSnapshot): IIntegrationEntity[] {
    const base = this.entityBase(snapshotArg);
    const player = snapshotArg.player;
    const available = snapshotArg.online;
    return [{
      id: this.mediaPlayerEntityId(snapshotArg),
      uniqueId: `frontier_silicon_${this.uniqueBase(snapshotArg)}`,
      integrationDomain: 'frontier_silicon',
      deviceId: this.deviceId(snapshotArg),
      platform: 'media_player',
      name: this.deviceName(snapshotArg.device),
      state: this.mediaState(snapshotArg, player),
      attributes: {
        deviceClass: 'speaker',
        radioId: snapshotArg.device.radioId,
        model: snapshotArg.device.model,
        version: snapshotArg.device.version,
        host: snapshotArg.device.host,
        port: snapshotArg.device.port,
        volumeLevel: this.volumeLevel(player),
        volume: player.volume,
        maxVolume: player.maxVolume,
        isVolumeMuted: player.muted,
        source: player.source,
        sourceList: snapshotArg.modes.map((modeArg) => modeArg.label),
        sourceModes: snapshotArg.modes,
        soundMode: player.soundMode?.label,
        soundModeList: snapshotArg.equalisers.map((equaliserArg) => equaliserArg.label),
        repeat: player.repeat,
        shuffle: player.shuffle,
        mediaContentType: 'channel',
        mediaTitle: player.mediaTitle,
        mediaName: player.mediaName,
        mediaText: player.mediaText,
        mediaArtist: player.mediaArtist,
        mediaAlbumName: player.mediaAlbum,
        mediaImageUrl: player.mediaImageUrl,
        mediaDuration: player.mediaDuration,
        mediaPosition: player.mediaPosition,
        presets: snapshotArg.presets,
      },
      available,
    }, {
      id: `sensor.${base}_frontier_silicon_media`,
      uniqueId: `frontier_silicon_${this.uniqueBase(snapshotArg)}_media`,
      integrationDomain: 'frontier_silicon',
      deviceId: this.deviceId(snapshotArg),
      platform: 'sensor',
      name: `${this.deviceName(snapshotArg.device)} Frontier Silicon Media`,
      state: player.mediaTitle || player.mediaName || 'None',
      attributes: {
        radioId: snapshotArg.device.radioId,
        mediaName: player.mediaName,
        mediaText: player.mediaText,
        mediaArtist: player.mediaArtist,
        mediaAlbum: player.mediaAlbum,
        mediaImageUrl: player.mediaImageUrl,
      },
      available,
    }, {
      id: `sensor.${base}_frontier_silicon_sources`,
      uniqueId: `frontier_silicon_${this.uniqueBase(snapshotArg)}_sources`,
      integrationDomain: 'frontier_silicon',
      deviceId: this.deviceId(snapshotArg),
      platform: 'sensor',
      name: `${this.deviceName(snapshotArg.device)} Frontier Silicon Sources`,
      state: snapshotArg.modes.length,
      attributes: {
        sourceList: snapshotArg.modes.map((modeArg) => modeArg.label),
        modes: snapshotArg.modes,
      },
      available,
    }, {
      id: `sensor.${base}_frontier_silicon_presets`,
      uniqueId: `frontier_silicon_${this.uniqueBase(snapshotArg)}_presets`,
      integrationDomain: 'frontier_silicon',
      deviceId: this.deviceId(snapshotArg),
      platform: 'sensor',
      name: `${this.deviceName(snapshotArg.device)} Frontier Silicon Presets`,
      state: snapshotArg.presets.length,
      attributes: {
        presets: snapshotArg.presets,
      },
      available,
    }];
  }

  public static entityDeviceId(snapshotArg: IFrontierSiliconSnapshot, entityIdArg: string): string | undefined {
    const entity = this.toEntities(snapshotArg).find((entityArg) => entityArg.id === entityIdArg);
    return entity?.deviceId;
  }

  public static mediaPlayerEntityId(snapshotArg: IFrontierSiliconSnapshot): string {
    return `media_player.${this.entityBase(snapshotArg)}`;
  }

  public static deviceId(snapshotArg: IFrontierSiliconSnapshot): string {
    return `frontier_silicon.radio.${this.uniqueBase(snapshotArg)}`;
  }

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

  private static uniqueBase(snapshotArg: IFrontierSiliconSnapshot): string {
    return this.slug(snapshotArg.device.radioId || snapshotArg.device.id || snapshotArg.device.host || this.deviceName(snapshotArg.device));
  }

  private static entityBase(snapshotArg: IFrontierSiliconSnapshot): string {
    return this.slug(this.deviceName(snapshotArg.device));
  }

  private static deviceName(deviceArg: IFrontierSiliconDeviceInfo): string {
    return deviceArg.name || deviceArg.host || 'Frontier Silicon';
  }

  private static mediaState(snapshotArg: IFrontierSiliconSnapshot, playerArg: IFrontierSiliconPlayerState): string {
    if (!snapshotArg.online || playerArg.power === false || playerArg.playState === 'off') {
      return 'off';
    }
    return playerArg.playState || 'unknown';
  }

  private static volumeLevel(playerArg: IFrontierSiliconPlayerState): number | undefined {
    if (typeof playerArg.volume !== 'number' || typeof playerArg.maxVolume !== 'number' || playerArg.maxVolume <= 0) {
      return undefined;
    }
    return Math.max(0, Math.min(1, playerArg.volume / playerArg.maxVolume));
  }

  private static volumePercent(playerArg: IFrontierSiliconPlayerState): number | undefined {
    const level = this.volumeLevel(playerArg);
    return typeof level === 'number' ? Math.round(level * 100) : undefined;
  }
}
