import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import { linkplayRepeatFromLoopMode, linkplaySourceName } from './linkplay.classes.client.js';
import type { ILinkplayMultiroomGroup, ILinkplaySnapshot, ILinkplaySpeaker } from './linkplay.types.js';

export class LinkplayMapper {
  public static toDevices(snapshotArg: ILinkplaySnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [];
    for (const speaker of snapshotArg.speakers) {
      devices.push({
        id: this.speakerDeviceId(speaker),
        integrationDomain: 'linkplay',
        name: this.speakerName(speaker),
        protocol: speaker.device.protocol === 'http' ? 'http' : 'unknown',
        manufacturer: speaker.device.manufacturer || 'Generic',
        model: speaker.device.model || speaker.device.modelId || 'LinkPlay Speaker',
        online: snapshotArg.online && speaker.available !== false,
        features: [
          { 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: 'preset_count', capability: 'media', name: 'Presets', readable: true, writable: true },
          { id: 'multiroom_group', capability: 'media', name: 'Multiroom group', readable: true, writable: true },
          { id: 'current_title', capability: 'media', name: 'Current title', readable: true, writable: false },
        ],
        state: [
          { featureId: 'playback', value: this.mediaState(snapshotArg, speaker), updatedAt },
          { featureId: 'volume', value: speaker.player.volume ?? null, updatedAt },
          { featureId: 'muted', value: speaker.player.muted ?? null, updatedAt },
          { featureId: 'source', value: this.currentSource(speaker) || null, updatedAt },
          { featureId: 'preset_count', value: (speaker.presets || snapshotArg.presets || []).length, updatedAt },
          { featureId: 'multiroom_group', value: this.groupForSpeaker(snapshotArg, speaker.uuid)?.id || null, updatedAt },
          { featureId: 'current_title', value: this.mediaTitle(speaker) || null, updatedAt },
        ],
        metadata: {
          uuid: speaker.uuid,
          host: speaker.device.host,
          port: speaker.device.port,
          project: speaker.device.project,
          modelId: speaker.device.modelId,
          macAddress: speaker.device.macAddress,
          hardwareVersion: speaker.device.hardwareVersion,
          firmwareVersion: speaker.device.firmwareVersion,
          viaIntegration: snapshotArg.source,
        },
      });
    }

    for (const group of snapshotArg.multirooms || []) {
      devices.push({
        id: this.multiroomDeviceId(group),
        integrationDomain: 'linkplay',
        name: group.name || 'LinkPlay Multiroom',
        protocol: 'http',
        manufacturer: 'LinkPlay',
        model: 'Multiroom Group',
        online: snapshotArg.online,
        features: [
          { id: 'members', capability: 'media', name: 'Members', readable: true, writable: true },
          { id: 'volume', capability: 'media', name: 'Group volume', readable: true, writable: true, unit: '%' },
          { id: 'muted', capability: 'media', name: 'Group muted', readable: true, writable: true },
        ],
        state: [
          { featureId: 'members', value: this.groupEntityIds(snapshotArg, group).length, updatedAt },
          { featureId: 'volume', value: group.volume ?? null, updatedAt },
          { featureId: 'muted', value: group.muted ?? null, updatedAt },
        ],
        metadata: {
          leaderUuid: group.leaderUuid,
          followerUuids: group.followerUuids,
          members: this.groupEntityIds(snapshotArg, group),
        },
      });
    }
    return devices;
  }

  public static toEntities(snapshotArg: ILinkplaySnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    for (const speaker of snapshotArg.speakers) {
      const base = this.speakerEntityBase(speaker);
      const available = snapshotArg.online && speaker.available !== false;
      const group = this.groupForSpeaker(snapshotArg, speaker.uuid);
      entities.push({
        id: this.speakerEntityId(speaker),
        uniqueId: `linkplay_${this.slug(speaker.uuid)}`,
        integrationDomain: 'linkplay',
        deviceId: this.speakerDeviceId(speaker),
        platform: 'media_player',
        name: this.speakerName(speaker),
        state: this.mediaState(snapshotArg, speaker),
        attributes: {
          deviceClass: 'receiver',
          speakerUuid: speaker.uuid,
          model: speaker.device.model,
          modelId: speaker.device.modelId,
          project: speaker.device.project,
          host: speaker.device.host,
          volumeLevel: this.volumeLevel(speaker),
          volume: speaker.player.volume,
          isVolumeMuted: speaker.player.muted,
          repeat: linkplayRepeatFromLoopMode(speaker.player.loopMode),
          shuffle: speaker.player.loopMode === '2',
          soundMode: speaker.player.equalizerMode,
          soundModeList: this.soundModeList(speaker),
          source: this.currentSource(speaker),
          sourceList: this.sourceList(snapshotArg, speaker),
          presets: speaker.presets || snapshotArg.presets || [],
          audioOutputHardwareMode: speaker.audioOutputHardwareMode,
          mediaContentType: 'music',
          mediaDuration: this.seconds(speaker.player.totalLengthMs),
          mediaPosition: this.seconds(speaker.player.currentPositionMs),
          mediaTitle: speaker.player.title,
          mediaArtist: speaker.player.artist,
          mediaAlbumName: speaker.player.album,
          mediaImageUrl: speaker.player.status === 'play' || speaker.player.status === 'pause' ? speaker.player.albumArtUrl : undefined,
          playMode: speaker.player.playMode,
          loopMode: speaker.player.loopMode,
          groupId: group?.id,
          groupRole: group ? group.leaderUuid === speaker.uuid ? 'leader' : 'follower' : undefined,
          groupMembers: group ? this.groupEntityIds(snapshotArg, group) : undefined,
        },
        available,
      });

      entities.push({
        id: `sensor.${base}_linkplay_media`,
        uniqueId: `linkplay_${this.slug(speaker.uuid)}_media`,
        integrationDomain: 'linkplay',
        deviceId: this.speakerDeviceId(speaker),
        platform: 'sensor',
        name: `${this.speakerName(speaker)} LinkPlay Media`,
        state: this.mediaTitle(speaker) || 'None',
        attributes: {
          speakerUuid: speaker.uuid,
          player: speaker.player,
        },
        available,
      });

      entities.push({
        id: `sensor.${base}_linkplay_sources`,
        uniqueId: `linkplay_${this.slug(speaker.uuid)}_sources`,
        integrationDomain: 'linkplay',
        deviceId: this.speakerDeviceId(speaker),
        platform: 'sensor',
        name: `${this.speakerName(speaker)} LinkPlay Sources`,
        state: this.sourceList(snapshotArg, speaker).length,
        attributes: {
          speakerUuid: speaker.uuid,
          sources: speaker.sources || snapshotArg.sources || [],
          sourceList: this.sourceList(snapshotArg, speaker),
        },
        available,
      });

      entities.push({
        id: `sensor.${base}_linkplay_presets`,
        uniqueId: `linkplay_${this.slug(speaker.uuid)}_presets`,
        integrationDomain: 'linkplay',
        deviceId: this.speakerDeviceId(speaker),
        platform: 'sensor',
        name: `${this.speakerName(speaker)} LinkPlay Presets`,
        state: (speaker.presets || snapshotArg.presets || []).length,
        attributes: {
          speakerUuid: speaker.uuid,
          presets: speaker.presets || snapshotArg.presets || [],
        },
        available,
      });

      if (group) {
        entities.push({
          id: `sensor.${base}_linkplay_multiroom`,
          uniqueId: `linkplay_${this.slug(speaker.uuid)}_multiroom`,
          integrationDomain: 'linkplay',
          deviceId: this.speakerDeviceId(speaker),
          platform: 'sensor',
          name: `${this.speakerName(speaker)} LinkPlay Multiroom`,
          state: group.leaderUuid === speaker.uuid ? 'leader' : 'follower',
          attributes: {
            speakerUuid: speaker.uuid,
            group,
            members: this.groupEntityIds(snapshotArg, group),
          },
          available,
        });
      }

      if (speaker.audioOutputHardwareMode) {
        entities.push({
          id: `select.${base}_audio_output_hardware_mode`,
          uniqueId: `linkplay_${this.slug(speaker.uuid)}_audio_output_hardware_mode`,
          integrationDomain: 'linkplay',
          deviceId: this.speakerDeviceId(speaker),
          platform: 'select',
          name: `${this.speakerName(speaker)} Audio output hardware mode`,
          state: speaker.audioOutputHardwareMode,
          attributes: {
            speakerUuid: speaker.uuid,
            options: ['optical', 'line_out', 'coaxial', 'headphones'],
          },
          available,
        });
      }
    }

    if ((snapshotArg.multirooms || []).length) {
      entities.push({
        id: `sensor.${this.slug(snapshotArg.speakers[0]?.name || 'linkplay')}_multiroom_groups`,
        uniqueId: `linkplay_${this.snapshotUniqueBase(snapshotArg)}_multiroom_groups`,
        integrationDomain: 'linkplay',
        deviceId: this.speakerDeviceId(snapshotArg.speakers[0]),
        platform: 'sensor',
        name: 'LinkPlay Multiroom Groups',
        state: (snapshotArg.multirooms || []).length,
        attributes: {
          groups: (snapshotArg.multirooms || []).map((groupArg) => ({
            ...groupArg,
            members: this.groupEntityIds(snapshotArg, groupArg),
          })),
        },
        available: snapshotArg.online,
      });
    }

    return entities;
  }

  public static entitySpeakerUuid(snapshotArg: ILinkplaySnapshot, entityIdArg: string): string | undefined {
    const entity = this.toEntities(snapshotArg).find((entityArg) => entityArg.id === entityIdArg);
    const speakerUuid = entity?.attributes?.speakerUuid;
    return typeof speakerUuid === 'string' ? speakerUuid : undefined;
  }

  public static speakerEntityId(speakerArg: ILinkplaySpeaker): string {
    return `media_player.${this.speakerEntityBase(speakerArg)}`;
  }

  public static speakerDeviceId(speakerArg: ILinkplaySpeaker | undefined): string {
    return `linkplay.speaker.${this.slug(speakerArg?.uuid || speakerArg?.device.uuid || speakerArg?.device.host || speakerArg?.name || 'linkplay')}`;
  }

  public static multiroomDeviceId(groupArg: ILinkplayMultiroomGroup): string {
    return `linkplay.multiroom.${this.slug(groupArg.id || groupArg.leaderUuid)}`;
  }

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

  private static mediaState(snapshotArg: ILinkplaySnapshot, speakerArg: ILinkplaySpeaker): string {
    if (!snapshotArg.online || speakerArg.available === false) {
      return 'off';
    }
    if (speakerArg.player.status === 'play') {
      return 'playing';
    }
    if (speakerArg.player.status === 'pause') {
      return 'paused';
    }
    if (speakerArg.player.status === 'load') {
      return 'buffering';
    }
    if (speakerArg.player.status === 'stop') {
      return 'idle';
    }
    return speakerArg.player.status || 'unknown';
  }

  private static currentSource(speakerArg: ILinkplaySpeaker): string | undefined {
    return speakerArg.player.source || linkplaySourceName(speakerArg.player.playMode);
  }

  private static sourceList(snapshotArg: ILinkplaySnapshot, speakerArg: ILinkplaySpeaker): string[] {
    const sources = speakerArg.sources || snapshotArg.sources || [];
    return [...new Set(sources.map((sourceArg) => sourceArg.name).filter(Boolean))];
  }

  private static soundModeList(speakerArg: ILinkplaySpeaker): string[] {
    if (speakerArg.device.manufacturer === 'WiiM') {
      return ['None', 'Flat', 'Acoustic', 'Bass Booster', 'Bass Reducer', 'Classical', 'Dance', 'Deep', 'Electronic', 'Hip-Hop', 'Jazz', 'Latin', 'Loudness', 'Lounge', 'Piano', 'Pop', 'R&B', 'Rock', 'Small Speakers', 'Spoken Word', 'Treble Booster', 'Treble Reducer', 'Vocal Booster'];
    }
    return ['None', 'Classic', 'Pop', 'Jazz', 'Vocal'];
  }

  private static volumeLevel(speakerArg: ILinkplaySpeaker): number | undefined {
    return typeof speakerArg.player.volume === 'number' ? Math.max(0, Math.min(1, speakerArg.player.volume / 100)) : undefined;
  }

  private static mediaTitle(speakerArg: ILinkplaySpeaker): string | undefined {
    return speakerArg.player.title || speakerArg.player.metaInfo?.metaData?.title || speakerArg.player.metaInfo?.metaData?.subtitle;
  }

  private static seconds(valueArg: number | undefined): number | undefined {
    return typeof valueArg === 'number' ? Math.floor(valueArg / 1000) : undefined;
  }

  private static groupForSpeaker(snapshotArg: ILinkplaySnapshot, speakerUuidArg: string): ILinkplayMultiroomGroup | undefined {
    return (snapshotArg.multirooms || []).find((groupArg) => groupArg.leaderUuid === speakerUuidArg || groupArg.followerUuids.includes(speakerUuidArg));
  }

  private static groupEntityIds(snapshotArg: ILinkplaySnapshot, groupArg: ILinkplayMultiroomGroup): string[] {
    return [groupArg.leaderUuid, ...groupArg.followerUuids]
      .map((uuidArg) => snapshotArg.speakers.find((speakerArg) => speakerArg.uuid === uuidArg))
      .filter((speakerArg): speakerArg is ILinkplaySpeaker => Boolean(speakerArg))
      .map((speakerArg) => this.speakerEntityId(speakerArg));
  }

  private static speakerEntityBase(speakerArg: ILinkplaySpeaker): string {
    return this.slug(this.speakerName(speakerArg));
  }

  private static speakerName(speakerArg: ILinkplaySpeaker): string {
    return speakerArg.name || speakerArg.device.name || speakerArg.uuid || 'LinkPlay';
  }

  private static snapshotUniqueBase(snapshotArg: ILinkplaySnapshot): string {
    return this.slug(snapshotArg.speakers[0]?.uuid || snapshotArg.speakers[0]?.device.host || 'linkplay');
  }
}
