import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IVolumioPlaylist, IVolumioPlaylistCollection, IVolumioSnapshot, IVolumioState } from './volumio.types.js';

export class VolumioMapper {
  public static toDevices(snapshotArg: IVolumioSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const state = snapshotArg.state;
    return [{
      id: this.deviceId(snapshotArg),
      integrationDomain: 'volumio',
      name: this.deviceName(snapshotArg),
      protocol: 'http',
      manufacturer: snapshotArg.deviceInfo.manufacturer || 'Volumio',
      model: snapshotArg.deviceInfo.model || snapshotArg.deviceInfo.hardware,
      online: snapshotArg.online,
      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: 'current_title', capability: 'media', name: 'Current title', readable: true, writable: false },
        { id: 'media_position', capability: 'media', name: 'Media position', readable: true, writable: true, unit: 's' },
        { id: 'media_duration', capability: 'media', name: 'Media duration', readable: true, writable: false, unit: 's' },
        { id: 'shuffle', capability: 'media', name: 'Shuffle', readable: true, writable: true },
        { id: 'repeat', capability: 'media', name: 'Repeat', readable: true, writable: true },
      ],
      state: [
        { featureId: 'playback', value: this.mediaState(snapshotArg), updatedAt },
        { featureId: 'volume', value: this.volumePercent(state) ?? null, updatedAt },
        { featureId: 'muted', value: this.booleanValue(state.mute) ?? null, updatedAt },
        { featureId: 'source', value: state.service || null, updatedAt },
        { featureId: 'current_title', value: state.title || null, updatedAt },
        { featureId: 'media_position', value: this.mediaPosition(state), updatedAt },
        { featureId: 'media_duration', value: this.numberValue(state.duration) ?? null, updatedAt },
        { featureId: 'shuffle', value: this.booleanValue(state.random) ?? null, updatedAt },
        { featureId: 'repeat', value: this.booleanValue(state.repeat) ?? null, updatedAt },
      ],
      metadata: {
        uuid: snapshotArg.deviceInfo.uuid,
        host: snapshotArg.deviceInfo.host,
        port: snapshotArg.deviceInfo.port,
        hardware: snapshotArg.deviceInfo.hardware,
        systemVersion: snapshotArg.deviceInfo.systemVersion,
        service: state.service,
        trackType: state.trackType,
        uri: state.uri,
      },
    }];
  }

  public static toEntities(snapshotArg: IVolumioSnapshot): IIntegrationEntity[] {
    const state = snapshotArg.state;
    const volume = this.volumePercent(state);
    return [{
      id: `media_player.${this.slug(this.deviceName(snapshotArg))}`,
      uniqueId: `volumio_${this.uniqueBase(snapshotArg)}`,
      integrationDomain: 'volumio',
      deviceId: this.deviceId(snapshotArg),
      platform: 'media_player',
      name: this.deviceName(snapshotArg),
      state: this.mediaState(snapshotArg),
      attributes: {
        volumeLevel: typeof volume === 'number' ? volume / 100 : undefined,
        volumePercent: volume,
        isVolumeMuted: this.booleanValue(state.mute),
        mediaContentId: state.uri,
        mediaContentType: this.mediaContentType(state),
        mediaDuration: this.numberValue(state.duration),
        mediaPosition: this.mediaPosition(state),
        mediaTitle: state.title,
        mediaArtist: state.artist,
        mediaAlbumName: state.album,
        mediaImageUrl: state.albumart,
        source: state.service,
        sourceList: this.playlistNames(snapshotArg.playlists),
        shuffle: this.booleanValue(state.random),
        repeat: this.booleanValue(state.repeat) ? 'all' : 'off',
        repeatSingle: this.booleanValue(state.repeatSingle),
        position: this.numberValue(state.position),
        service: state.service,
        trackType: state.trackType,
        sampleRate: this.numberValue(state.samplerate) ?? state.samplerate,
        bitDepth: this.numberValue(state.bitdepth) ?? state.bitdepth,
        bitrate: this.numberValue(state.bitrate) ?? state.bitrate,
        channels: this.numberValue(state.channels) ?? state.channels,
        consume: this.booleanValue(state.consume),
        volatile: this.booleanValue(state.volatile),
      },
      available: snapshotArg.online,
    }];
  }

  public static deviceId(snapshotArg: IVolumioSnapshot): string {
    return `volumio.device.${this.uniqueBase(snapshotArg)}`;
  }

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

  private static mediaState(snapshotArg: IVolumioSnapshot): string {
    if (!snapshotArg.online) {
      return 'off';
    }
    const status = snapshotArg.state.status;
    if (status === 'play') {
      return 'playing';
    }
    if (status === 'pause') {
      return 'paused';
    }
    return 'idle';
  }

  private static mediaContentType(stateArg: IVolumioState): string {
    if (stateArg.trackType === 'webradio') {
      return 'channel';
    }
    return stateArg.trackType || 'music';
  }

  private static mediaPosition(stateArg: IVolumioState): number | null {
    const seek = this.numberValue(stateArg.seek);
    if (typeof seek !== 'number') {
      return null;
    }
    const duration = this.numberValue(stateArg.duration);
    if (seek > 1000 && (duration === undefined || seek > duration + 5)) {
      return Math.round(seek / 1000);
    }
    return seek;
  }

  private static volumePercent(stateArg: IVolumioState): number | undefined {
    const value = this.numberValue(stateArg.volume);
    if (typeof value !== 'number') {
      return undefined;
    }
    return Math.max(0, Math.min(100, Math.round(value)));
  }

  private static playlistNames(playlistsArg: IVolumioPlaylistCollection | undefined): string[] {
    if (!playlistsArg) {
      return [];
    }
    if (Array.isArray(playlistsArg)) {
      return playlistsArg.map((itemArg) => this.playlistName(itemArg)).filter((itemArg): itemArg is string => Boolean(itemArg));
    }
    if (Array.isArray(playlistsArg.playlists)) {
      return playlistsArg.playlists.map((itemArg) => this.playlistName(itemArg)).filter((itemArg): itemArg is string => Boolean(itemArg));
    }
    if (Array.isArray(playlistsArg.lists)) {
      return playlistsArg.lists.flatMap((listArg) => (listArg.items || []).map((itemArg) => this.playlistName(itemArg))).filter((itemArg): itemArg is string => Boolean(itemArg));
    }
    if (Array.isArray(playlistsArg.navigation?.lists)) {
      return playlistsArg.navigation.lists.flatMap((listArg) => (listArg.items || []).map((itemArg) => this.playlistName(itemArg))).filter((itemArg): itemArg is string => Boolean(itemArg));
    }
    return [];
  }

  private static playlistName(valueArg: string | IVolumioPlaylist): string | undefined {
    if (typeof valueArg === 'string') {
      return valueArg;
    }
    return valueArg.name || valueArg.title;
  }

  private static uniqueBase(snapshotArg: IVolumioSnapshot): string {
    return this.slug(snapshotArg.deviceInfo.uuid || snapshotArg.deviceInfo.id || snapshotArg.deviceInfo.host || this.deviceName(snapshotArg));
  }

  private static deviceName(snapshotArg: IVolumioSnapshot): string {
    return snapshotArg.deviceInfo.name || snapshotArg.systemInfo?.name || 'Volumio';
  }

  private static numberValue(valueArg: unknown): number | undefined {
    if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
      return valueArg;
    }
    if (typeof valueArg === 'string' && valueArg.trim()) {
      const parsed = Number(valueArg);
      return Number.isFinite(parsed) ? parsed : undefined;
    }
    return undefined;
  }

  private static booleanValue(valueArg: unknown): boolean | undefined {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (typeof valueArg === 'string') {
      const normalized = valueArg.toLowerCase();
      if (normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on') {
        return true;
      }
      if (normalized === 'false' || normalized === '0' || normalized === 'no' || normalized === 'off') {
        return false;
      }
    }
    return undefined;
  }
}
