import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IJellyfinMediaItem, IJellyfinSession, IJellyfinSnapshot } from './jellyfin.types.js';

const contentTypeMap: Record<string, string> = {
  Audio: 'music',
  Episode: 'tvshow',
  Movie: 'movie',
  Series: 'tvshow',
  Season: 'season',
  Video: 'video',
  MusicAlbum: 'album',
  MusicArtist: 'artist',
  CollectionFolder: 'collection',
};

export class JellyfinMapper {
  public static toDevices(snapshotArg: IJellyfinSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [
      {
        id: this.serverDeviceId(snapshotArg),
        integrationDomain: 'jellyfin',
        name: this.serverName(snapshotArg),
        protocol: 'http',
        manufacturer: 'Jellyfin',
        model: snapshotArg.server.ProductName || 'Jellyfin Server',
        online: snapshotArg.online,
        features: [
          { id: 'active_clients', capability: 'sensor', name: 'Active clients', readable: true, writable: false },
          { id: 'playing_clients', capability: 'sensor', name: 'Playing clients', readable: true, writable: false },
        ],
        state: [
          { featureId: 'active_clients', value: this.activeSessions(snapshotArg).length, updatedAt },
          { featureId: 'playing_clients', value: this.nowPlayingCount(snapshotArg), updatedAt },
        ],
        metadata: {
          serverId: this.serverId(snapshotArg),
          version: snapshotArg.server.Version,
          localAddress: snapshotArg.server.LocalAddress,
          wanAddress: snapshotArg.server.WanAddress,
        },
      },
    ];

    for (const session of this.activeSessions(snapshotArg)) {
      const playState = session.PlayState;
      const nowPlaying = session.NowPlayingItem;
      const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
        { 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: 'current_title', capability: 'media', name: 'Current title', readable: true, writable: false },
      ];
      if (this.supportsRemote(session)) {
        features.push({ id: 'remote_command', capability: 'media', name: 'Remote command', readable: false, writable: true });
      }

      devices.push({
        id: this.sessionDeviceId(session),
        integrationDomain: 'jellyfin',
        name: this.sessionName(session),
        protocol: 'http',
        manufacturer: 'Jellyfin',
        model: session.Client,
        online: snapshotArg.online && session.IsActive !== false,
        features,
        state: [
          { featureId: 'playback', value: this.playbackState(session, snapshotArg.online), updatedAt: this.sessionUpdatedAt(session, updatedAt) },
          { featureId: 'volume', value: typeof playState?.VolumeLevel === 'number' ? playState.VolumeLevel : null, updatedAt: this.sessionUpdatedAt(session, updatedAt) },
          { featureId: 'muted', value: typeof playState?.IsMuted === 'boolean' ? playState.IsMuted : null, updatedAt: this.sessionUpdatedAt(session, updatedAt) },
          { featureId: 'current_title', value: nowPlaying?.Name || null, updatedAt: this.sessionUpdatedAt(session, updatedAt) },
        ],
        metadata: {
          serverId: this.serverId(snapshotArg),
          sessionId: session.Id,
          deviceId: session.DeviceId,
          userId: session.UserId,
          userName: session.UserName,
          client: session.Client,
          applicationVersion: session.ApplicationVersion,
          supportsRemoteControl: this.supportsRemote(session),
          supportedCommands: session.Capabilities?.SupportedCommands || [],
        },
      });
    }

    return devices;
  }

  public static toEntities(snapshotArg: IJellyfinSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [
      {
        id: `sensor.${this.slug(this.serverName(snapshotArg))}_active_clients`,
        uniqueId: `jellyfin_${this.slug(this.serverId(snapshotArg))}_active_clients`,
        integrationDomain: 'jellyfin',
        deviceId: this.serverDeviceId(snapshotArg),
        platform: 'sensor',
        name: `${this.serverName(snapshotArg)} Active clients`,
        state: this.nowPlayingCount(snapshotArg),
        attributes: {
          activeSessions: this.activeSessions(snapshotArg).length,
        },
        available: snapshotArg.online,
      },
    ];

    for (const session of this.activeSessions(snapshotArg)) {
      const item = session.NowPlayingItem;
      entities.push({
        id: `media_player.${this.slug(this.sessionName(session))}`,
        uniqueId: `jellyfin_${this.slug(this.serverId(snapshotArg))}_${this.slug(session.Id || session.DeviceId || this.sessionName(session))}`,
        integrationDomain: 'jellyfin',
        deviceId: this.sessionDeviceId(session),
        platform: 'media_player',
        name: this.sessionName(session),
        state: this.playbackState(session, snapshotArg.online),
        attributes: {
          sessionId: session.Id,
          deviceId: session.DeviceId,
          userName: session.UserName,
          clientName: session.Client,
          applicationVersion: session.ApplicationVersion,
          supportsRemoteControl: this.supportsRemote(session),
          supportedCommands: session.Capabilities?.SupportedCommands || [],
          volumeLevel: typeof session.PlayState?.VolumeLevel === 'number' ? session.PlayState.VolumeLevel / 100 : undefined,
          isVolumeMuted: session.PlayState?.IsMuted,
          mediaContentId: item?.Id,
          mediaContentType: this.mediaContentType(item),
          mediaDuration: this.ticksToSeconds(item?.RunTimeTicks),
          mediaPosition: this.ticksToSeconds(session.PlayState?.PositionTicks),
          mediaPositionUpdatedAt: session.LastPlaybackCheckIn,
          mediaTitle: item?.Name,
          mediaSeriesTitle: item?.SeriesName,
          mediaSeason: item?.ParentIndexNumber,
          mediaEpisode: item?.IndexNumber,
          mediaAlbumName: item?.Album,
          mediaArtist: item?.Artists?.[0],
          mediaAlbumArtist: item?.AlbumArtist,
          mediaTrack: item?.IndexNumber,
        },
        available: snapshotArg.online && session.IsActive !== false,
      });
    }

    return entities;
  }

  public static activeSessions(snapshotArg: IJellyfinSnapshot): IJellyfinSession[] {
    return (snapshotArg.sessions || []).filter((sessionArg) => {
      return Boolean(sessionArg.Id || sessionArg.DeviceId) && (sessionArg.IsActive !== false || Boolean(sessionArg.NowPlayingItem));
    });
  }

  public static sessionDeviceId(sessionArg: IJellyfinSession): string {
    return `jellyfin.session.${this.slug(sessionArg.DeviceId || sessionArg.Id || this.sessionName(sessionArg))}`;
  }

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

  private static serverDeviceId(snapshotArg: IJellyfinSnapshot): string {
    return `jellyfin.server.${this.slug(this.serverId(snapshotArg))}`;
  }

  private static serverId(snapshotArg: IJellyfinSnapshot): string {
    return snapshotArg.server.Id || snapshotArg.server.ServerId || snapshotArg.server.Name || snapshotArg.server.ServerName || 'jellyfin';
  }

  private static serverName(snapshotArg: IJellyfinSnapshot): string {
    return snapshotArg.server.Name || snapshotArg.server.ServerName || 'Jellyfin';
  }

  private static sessionName(sessionArg: IJellyfinSession): string {
    return sessionArg.DeviceName || sessionArg.Client || sessionArg.DeviceId || sessionArg.Id || 'Jellyfin Client';
  }

  private static playbackState(sessionArg: IJellyfinSession, serverOnlineArg: boolean): string {
    if (!serverOnlineArg || sessionArg.IsActive === false) {
      return 'off';
    }
    if (sessionArg.PlayState?.IsPaused) {
      return 'paused';
    }
    if (sessionArg.NowPlayingItem) {
      return 'playing';
    }
    return 'idle';
  }

  private static supportsRemote(sessionArg: IJellyfinSession): boolean {
    return sessionArg.SupportsRemoteControl === true || Boolean(sessionArg.Capabilities?.SupportedCommands?.length);
  }

  private static nowPlayingCount(snapshotArg: IJellyfinSnapshot): number {
    return this.activeSessions(snapshotArg).filter((sessionArg) => Boolean(sessionArg.NowPlayingItem)).length;
  }

  private static mediaContentType(itemArg: IJellyfinMediaItem | undefined): string | undefined {
    if (!itemArg) {
      return undefined;
    }
    const type = itemArg.Type || itemArg.MediaType;
    return type ? contentTypeMap[type] || type.toLowerCase() : undefined;
  }

  private static ticksToSeconds(ticksArg: number | undefined): number | undefined {
    return typeof ticksArg === 'number' ? Math.floor(ticksArg / 10000000) : undefined;
  }

  private static sessionUpdatedAt(sessionArg: IJellyfinSession, fallbackArg: string): string {
    return sessionArg.LastPlaybackCheckIn || sessionArg.LastActivityDate || fallbackArg;
  }
}
