import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IDirectvLocation, IDirectvLocationState, IDirectvProgram, IDirectvSnapshot } from './directv.types.js';
import { directvDefaultDevice, directvDefaultName, directvDomain } from './directv.types.js';

export class DirectvMapper {
  public static toDevices(snapshotArg: IDirectvSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    return snapshotArg.locations.map((locationArg) => {
      const state = this.stateForLocation(snapshotArg, locationArg.address);
      const program = state?.program;
      return {
        id: this.locationDeviceId(snapshotArg, locationArg),
        integrationDomain: directvDomain,
        name: this.locationName(locationArg),
        protocol: 'http',
        manufacturer: snapshotArg.device.brand || 'DirecTV',
        model: locationArg.client ? 'DirecTV client' : 'DirecTV receiver',
        online: Boolean(state?.available),
        features: [
          { id: 'power', capability: 'media', name: 'Power', readable: true, writable: true },
          { id: 'playback', capability: 'media', name: 'Playback', readable: true, writable: true },
          { id: 'channel', capability: 'media', name: 'Channel', readable: true, writable: true },
          { id: 'current_title', capability: 'media', name: 'Current title', readable: true, writable: false },
          { id: 'recording', capability: 'sensor', name: 'Recording', readable: true, writable: false },
          { id: 'remote_command', capability: 'media', name: 'Remote command', readable: false, writable: true },
        ],
        state: [
          { featureId: 'power', value: this.powerState(state), updatedAt },
          { featureId: 'playback', value: this.mediaState(state), updatedAt },
          { featureId: 'channel', value: program?.channel || null, updatedAt },
          { featureId: 'current_title', value: this.mediaTitle(program) || null, updatedAt },
          { featureId: 'recording', value: program?.recording ?? null, updatedAt },
        ],
        metadata: this.cleanAttributes({
          receiverId: snapshotArg.device.receiverId,
          locationAddress: locationArg.address,
          client: locationArg.client,
          viaDevice: locationArg.client ? this.receiverIdentity(snapshotArg) : undefined,
          host: snapshotArg.device.host,
          port: snapshotArg.device.port,
          softwareVersion: snapshotArg.device.version,
          source: snapshotArg.source,
          status: state?.status,
          authorized: state?.authorized,
          error: state?.error || snapshotArg.error,
        }),
      };
    });
  }

  public static toEntities(snapshotArg: IDirectvSnapshot): IIntegrationEntity[] {
    return snapshotArg.locations.map((locationArg) => {
      const state = this.stateForLocation(snapshotArg, locationArg.address);
      const program = state?.program;
      const name = this.locationName(locationArg);
      return {
        id: this.mediaPlayerEntityId(snapshotArg, locationArg),
        uniqueId: `directv_${this.uniqueBase(snapshotArg, locationArg)}_media_player`,
        integrationDomain: directvDomain,
        deviceId: this.locationDeviceId(snapshotArg, locationArg),
        platform: 'media_player',
        name,
        state: this.mediaState(state),
        attributes: this.cleanAttributes({
          receiverId: snapshotArg.device.receiverId,
          locationAddress: locationArg.address,
          isClient: locationArg.client,
          source: snapshotArg.source,
          mediaContentId: program?.programId,
          mediaContentType: program?.programType,
          mediaDuration: program?.duration,
          mediaPosition: state?.standby ? undefined : program?.position,
          mediaPositionUpdatedAt: state?.positionUpdatedAt,
          mediaTitle: this.mediaTitle(program),
          mediaArtist: program?.musicArtist,
          mediaAlbumName: program?.musicAlbum,
          mediaSeriesTitle: program?.episodeTitle,
          mediaChannel: program?.channelName && program?.channel ? `${program.channelName} (${program.channel})` : program?.channel,
          sourceChannel: program?.channel,
          mediaCurrentlyRecording: program?.recording,
          mediaRating: program?.rating,
          mediaRecorded: state?.standby ? undefined : program?.recorded,
          mediaStartTime: program?.startTime,
          status: state?.status,
          authorized: state?.authorized,
          supportedFeatures: this.supportedFeatures(locationArg),
          error: state?.error || snapshotArg.error,
        }),
        available: Boolean(state?.available),
      };
    });
  }

  public static locationDeviceId(snapshotArg: IDirectvSnapshot, locationArg: IDirectvLocation): string {
    return `directv.device.${this.uniqueBase(snapshotArg, locationArg)}`;
  }

  public static mediaPlayerEntityId(snapshotArg: IDirectvSnapshot, locationArg: IDirectvLocation): string {
    return `media_player.${this.slug(`${this.locationName(locationArg)} ${this.uniqueBase(snapshotArg, locationArg)}`)}`;
  }

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

  public static mediaState(stateArg: IDirectvLocationState | undefined): string {
    if (!stateArg?.available) {
      return 'unavailable';
    }
    if (stateArg.standby) {
      return 'off';
    }
    if (stateArg.paused) {
      return 'paused';
    }
    return 'playing';
  }

  private static powerState(stateArg: IDirectvLocationState | undefined): string {
    if (!stateArg?.available) {
      return 'unavailable';
    }
    return stateArg.standby ? 'off' : 'on';
  }

  private static mediaTitle(programArg: IDirectvProgram | undefined): string | undefined {
    if (!programArg) {
      return undefined;
    }
    return programArg.programType === 'music' ? programArg.musicTitle : programArg.title;
  }

  private static supportedFeatures(locationArg: IDirectvLocation): string[] {
    const common = ['pause', 'play_media', 'stop', 'next_track', 'previous_track', 'play', 'remote_send_command'];
    return locationArg.client ? common : ['turn_on', 'turn_off', ...common];
  }

  private static stateForLocation(snapshotArg: IDirectvSnapshot, addressArg: string): IDirectvLocationState | undefined {
    return snapshotArg.states.find((stateArg) => stateArg.address === addressArg);
  }

  private static uniqueBase(snapshotArg: IDirectvSnapshot, locationArg: IDirectvLocation): string {
    const identity = locationArg.address !== directvDefaultDevice
      ? locationArg.address
      : this.receiverIdentity(snapshotArg);
    return this.slug(identity || this.locationName(locationArg));
  }

  private static receiverIdentity(snapshotArg: IDirectvSnapshot): string {
    return snapshotArg.device.receiverId || snapshotArg.device.host || snapshotArg.device.name || directvDefaultName;
  }

  private static locationName(locationArg: IDirectvLocation): string {
    return locationArg.name || (locationArg.client ? `DirecTV Client ${locationArg.address}` : directvDefaultName);
  }

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