import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type {
  IYamahaMusiccastCapability,
  IYamahaMusiccastRangeStep,
  IYamahaMusiccastSnapshot,
  IYamahaMusiccastZoneState,
} from './yamaha_musiccast.types.js';

export class YamahaMusiccastMapper {
  public static toDevices(snapshotArg: IYamahaMusiccastSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = new Date().toISOString();
    return snapshotArg.zones.map((zoneArg) => {
      const capabilities = this.capabilitiesForZone(zoneArg);
      const volumePercent = this.volumePercent(zoneArg);
      const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
        { id: 'power', capability: 'media', name: 'Power', readable: true, writable: true },
        { id: 'playback', capability: 'media', name: 'Playback', readable: true, writable: true },
        { id: 'source', capability: 'media', name: 'Source', 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 },
      ];
      const state: plugins.shxInterfaces.data.IDeviceState[] = [
        { featureId: 'power', value: this.powerState(zoneArg), updatedAt },
        { featureId: 'playback', value: this.mediaState(snapshotArg, zoneArg), updatedAt },
        { featureId: 'source', value: zoneArg.input || null, updatedAt },
        { featureId: 'volume', value: typeof volumePercent === 'number' ? volumePercent : null, updatedAt },
        { featureId: 'muted', value: typeof zoneArg.muted === 'boolean' ? zoneArg.muted : null, updatedAt },
      ];

      if (zoneArg.soundProgram || zoneArg.soundProgramList?.length) {
        features.push({ id: 'sound_program', capability: 'media', name: 'Sound Program', readable: true, writable: true });
        state.push({ featureId: 'sound_program', value: zoneArg.soundProgram || null, updatedAt });
      }

      for (const capability of capabilities) {
        features.push({
          id: `capability_${capability.id}`,
          capability: capability.type === 'switch' ? 'switch' : 'media',
          name: capability.name,
          readable: true,
          writable: true,
          unit: capability.type === 'number' ? this.unitForCapability(capability) : undefined,
        });
        state.push({ featureId: `capability_${capability.id}`, value: this.deviceStateValue(capability.current), updatedAt });
      }

      return {
        id: this.deviceId(snapshotArg, zoneArg),
        integrationDomain: 'yamaha_musiccast',
        name: this.deviceName(snapshotArg, zoneArg),
        protocol: 'http',
        manufacturer: 'Yamaha Corporation',
        model: snapshotArg.deviceInfo.model_name,
        online: zoneArg.available !== false,
        features,
        state,
        metadata: {
          host: snapshotArg.networkStatus?.ip_address,
          zone: zoneArg.zone,
          systemId: snapshotArg.deviceInfo.system_id,
          deviceId: snapshotArg.deviceInfo.device_id,
          serialNumber: snapshotArg.deviceInfo.serial_number,
          systemVersion: snapshotArg.deviceInfo.system_version,
          apiVersion: snapshotArg.deviceInfo.api_version,
          networkName: snapshotArg.networkStatus?.network_name,
          macAddresses: snapshotArg.networkStatus?.mac_address,
          viaDeviceId: zoneArg.zone === 'main' ? undefined : this.mainDeviceId(snapshotArg),
        },
      };
    });
  }

  public static toEntities(snapshotArg: IYamahaMusiccastSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    for (const zone of snapshotArg.zones) {
      const base = this.entityBase(snapshotArg, zone);
      entities.push({
        id: `media_player.${base}`,
        uniqueId: `yamaha_musiccast_${this.uniqueBase(snapshotArg)}_${this.slug(zone.zone)}`,
        integrationDomain: 'yamaha_musiccast',
        deviceId: this.deviceId(snapshotArg, zone),
        platform: 'media_player',
        name: this.deviceName(snapshotArg, zone),
        state: this.mediaState(snapshotArg, zone),
        attributes: {
          deviceClass: 'speaker',
          zone: zone.zone,
          power: zone.power,
          volumeLevel: this.volumeLevel(zone),
          volume: zone.volume,
          minVolume: zone.minVolume,
          maxVolume: zone.maxVolume,
          isVolumeMuted: zone.muted,
          source: this.sourceLabel(snapshotArg, zone.input, zone),
          sourceId: zone.input,
          sourceList: this.sourceList(snapshotArg, zone),
          soundMode: zone.soundProgram,
          soundModeList: zone.soundProgramList,
          mediaTitle: this.mediaTitle(snapshotArg, zone),
          mediaArtist: this.mediaArtist(snapshotArg, zone),
          mediaAlbumName: snapshotArg.netusb?.album,
          mediaImageUrl: snapshotArg.netusb?.albumart_url,
          mediaDuration: snapshotArg.netusb?.total_time,
          mediaPosition: snapshotArg.netusb?.play_time,
          repeat: this.isNetusbZone(snapshotArg, zone) ? snapshotArg.netusb?.repeat : undefined,
          shuffle: this.isNetusbZone(snapshotArg, zone) ? snapshotArg.netusb?.shuffle === 'on' : undefined,
          groupId: snapshotArg.distribution?.group_id,
          groupName: snapshotArg.distribution?.group_name,
          groupRole: snapshotArg.distribution?.role,
        },
        available: zone.available !== false,
      });

      for (const capability of this.capabilitiesForZone(zone)) {
        const entityBase = `${base}_${this.slug(capability.id)}`;
        entities.push({
          id: `${capability.type}.${entityBase}`,
          uniqueId: `yamaha_musiccast_${this.uniqueBase(snapshotArg)}_${this.slug(zone.zone)}_${this.slug(capability.id)}`,
          integrationDomain: 'yamaha_musiccast',
          deviceId: this.deviceId(snapshotArg, zone),
          platform: capability.type,
          name: `${this.deviceName(snapshotArg, zone)} ${capability.name}`,
          state: this.entityState(capability),
          attributes: this.capabilityAttributes(capability, zone),
          available: zone.available !== false,
        });
      }
    }
    return entities;
  }

  public static capabilitiesForZone(zoneArg: IYamahaMusiccastZoneState): IYamahaMusiccastCapability[] {
    const explicit = zoneArg.capabilities || [];
    const generated: IYamahaMusiccastCapability[] = [];

    this.pushSwitch(generated, zoneArg, 'enhancer', 'Enhancer', zoneArg.enhancer);
    this.pushSwitch(generated, zoneArg, 'pure_direct', 'Pure Direct', zoneArg.pureDirect);
    this.pushSwitch(generated, zoneArg, 'extra_bass', 'Extra Bass', zoneArg.extraBass);
    this.pushSwitch(generated, zoneArg, 'bass_extension', 'Bass Extension', zoneArg.bassExtension);
    this.pushSwitch(generated, zoneArg, 'adaptive_drc', 'Adaptive DRC', zoneArg.adaptiveDrc);
    this.pushSwitch(generated, zoneArg, 'clear_voice', 'Clear Voice', zoneArg.clearVoice);
    this.pushSwitch(generated, zoneArg, 'surround_3d', '3D Surround', zoneArg.surround3d);
    this.pushSwitch(generated, zoneArg, 'mono', 'Mono', zoneArg.mono);

    this.pushSelect(generated, zoneArg, 'sleep', 'Sleep Timer', zoneArg.sleep === 0 ? 'off' : typeof zoneArg.sleep === 'number' ? `${zoneArg.sleep}_min` : undefined, ['off', '30_min', '60_min', '90_min', '120_min']);
    this.pushSelect(generated, zoneArg, 'tone_control_mode', 'Tone Control Mode', zoneArg.toneControl?.mode, zoneArg.toneControlModeList);
    this.pushSelect(generated, zoneArg, 'equalizer_mode', 'Equalizer Mode', zoneArg.equalizer?.mode, zoneArg.equalizerModeList);
    this.pushSelect(generated, zoneArg, 'surr_decoder_type', 'Surround Decoder Type', zoneArg.surrDecoderType, zoneArg.surrDecoderTypeList);
    this.pushSelect(generated, zoneArg, 'link_control', 'Link Control', zoneArg.linkControl, zoneArg.linkControlList);
    this.pushSelect(generated, zoneArg, 'link_audio_delay', 'Link Audio Delay', zoneArg.linkAudioDelay, zoneArg.linkAudioDelayList);
    this.pushSelect(generated, zoneArg, 'link_audio_quality', 'Link Audio Quality', zoneArg.linkAudioQuality, zoneArg.linkAudioQualityList);

    this.pushNumber(generated, zoneArg, 'tone_control_bass', 'Tone Control Bass', zoneArg.toneControl?.bass, this.range(zoneArg, 'tone_control'));
    this.pushNumber(generated, zoneArg, 'tone_control_treble', 'Tone Control Treble', zoneArg.toneControl?.treble, this.range(zoneArg, 'tone_control'));
    this.pushNumber(generated, zoneArg, 'equalizer_low', 'Equalizer Low', zoneArg.equalizer?.low, this.range(zoneArg, 'equalizer'));
    this.pushNumber(generated, zoneArg, 'equalizer_mid', 'Equalizer Mid', zoneArg.equalizer?.mid, this.range(zoneArg, 'equalizer'));
    this.pushNumber(generated, zoneArg, 'equalizer_high', 'Equalizer High', zoneArg.equalizer?.high, this.range(zoneArg, 'equalizer'));
    this.pushNumber(generated, zoneArg, 'balance', 'Balance', zoneArg.balance, this.range(zoneArg, 'balance'));
    this.pushNumber(generated, zoneArg, 'dialogue_level', 'Dialogue Level', zoneArg.dialogueLevel, this.range(zoneArg, 'dialogue_level'));
    this.pushNumber(generated, zoneArg, 'dialogue_lift', 'Dialogue Lift', zoneArg.dialogueLift, this.range(zoneArg, 'dialogue_lift'));
    this.pushNumber(generated, zoneArg, 'dts_dialogue_control', 'DTS Dialogue Control', zoneArg.dtsDialogueControl, this.range(zoneArg, 'dts_dialogue_control'));
    this.pushNumber(generated, zoneArg, 'subwoofer_volume', 'Subwoofer Volume', zoneArg.subwooferVolume, this.range(zoneArg, 'subwoofer_volume'));

    const byId = new Map<string, IYamahaMusiccastCapability>();
    for (const capability of [...generated, ...explicit]) {
      byId.set(capability.id, { category: 'config', zoneId: zoneArg.zone, ...capability });
    }
    return [...byId.values()];
  }

  private static pushSwitch(listArg: IYamahaMusiccastCapability[], zoneArg: IYamahaMusiccastZoneState, idArg: string, nameArg: string, valueArg: boolean | undefined): void {
    if (typeof valueArg === 'boolean') {
      listArg.push({ id: idArg, name: nameArg, type: 'switch', zoneId: zoneArg.zone, current: valueArg, category: 'config' });
    }
  }

  private static pushSelect(listArg: IYamahaMusiccastCapability[], zoneArg: IYamahaMusiccastZoneState, idArg: string, nameArg: string, valueArg: string | undefined, optionsArg: string[] | undefined): void {
    if (valueArg !== undefined || optionsArg?.length) {
      listArg.push({ id: idArg, name: nameArg, type: 'select', zoneId: zoneArg.zone, current: valueArg ?? null, options: optionsArg, category: 'config' });
    }
  }

  private static pushNumber(listArg: IYamahaMusiccastCapability[], zoneArg: IYamahaMusiccastZoneState, idArg: string, nameArg: string, valueArg: number | undefined, rangeArg: IYamahaMusiccastRangeStep | undefined): void {
    if (typeof valueArg === 'number' || rangeArg) {
      listArg.push({ id: idArg, name: nameArg, type: 'number', zoneId: zoneArg.zone, current: valueArg ?? null, range: rangeArg, category: 'config' });
    }
  }

  private static entityState(capabilityArg: IYamahaMusiccastCapability): unknown {
    if (capabilityArg.type === 'switch') {
      return Boolean(capabilityArg.current);
    }
    return capabilityArg.current ?? null;
  }

  private static capabilityAttributes(capabilityArg: IYamahaMusiccastCapability, zoneArg: IYamahaMusiccastZoneState): Record<string, unknown> {
    return {
      zone: zoneArg.zone,
      capabilityId: capabilityArg.id,
      category: capabilityArg.category,
      options: this.optionValues(capabilityArg.options),
      nativeMinValue: capabilityArg.range?.min,
      nativeMaxValue: capabilityArg.range?.max,
      nativeStep: capabilityArg.range?.step,
    };
  }

  private static optionValues(optionsArg: Record<string, string> | string[] | undefined): string[] | undefined {
    if (!optionsArg) {
      return undefined;
    }
    return Array.isArray(optionsArg) ? optionsArg : Object.values(optionsArg);
  }

  private static mediaState(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): string {
    if (this.powerState(zoneArg) === 'off') {
      return 'off';
    }
    const explicitState = zoneArg.state?.toLowerCase();
    if (explicitState === 'playing' || explicitState === 'paused' || explicitState === 'idle') {
      return explicitState;
    }
    if (this.isNetusbZone(snapshotArg, zoneArg)) {
      if (snapshotArg.netusb?.playback === 'pause') {
        return 'paused';
      }
      if (snapshotArg.netusb?.playback === 'stop') {
        return 'idle';
      }
    }
    return 'playing';
  }

  private static powerState(zoneArg: IYamahaMusiccastZoneState): string {
    const power = zoneArg.power?.toLowerCase();
    return power === 'on' ? 'on' : 'off';
  }

  private static volumeLevel(zoneArg: IYamahaMusiccastZoneState): number | undefined {
    if (typeof zoneArg.volumeLevel === 'number') {
      return Math.max(0, Math.min(1, zoneArg.volumeLevel));
    }
    if (typeof zoneArg.volume === 'number' && typeof zoneArg.minVolume === 'number' && typeof zoneArg.maxVolume === 'number' && zoneArg.maxVolume > zoneArg.minVolume) {
      return Math.max(0, Math.min(1, (zoneArg.volume - zoneArg.minVolume) / (zoneArg.maxVolume - zoneArg.minVolume)));
    }
    return undefined;
  }

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

  private static sourceList(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): string[] | undefined {
    return zoneArg.inputList?.map((inputArg) => this.sourceLabel(snapshotArg, inputArg, zoneArg)).filter((valueArg): valueArg is string => Boolean(valueArg));
  }

  private static sourceLabel(snapshotArg: IYamahaMusiccastSnapshot, inputArg: string | undefined, zoneArg?: IYamahaMusiccastZoneState): string | undefined {
    if (!inputArg) {
      return undefined;
    }
    return zoneArg?.sourceMap?.[inputArg] || snapshotArg.inputNames?.[inputArg] || (zoneArg?.input === inputArg ? zoneArg.inputText : undefined) || this.titleize(inputArg);
  }

  private static mediaTitle(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): string | undefined {
    if (this.isNetusbZone(snapshotArg, zoneArg)) {
      return snapshotArg.netusb?.track || this.sourceLabel(snapshotArg, zoneArg.input, zoneArg);
    }
    if (zoneArg.input === 'tuner') {
      return snapshotArg.tuner?.rds?.radio_text_a || snapshotArg.tuner?.rds?.program_service || this.sourceLabel(snapshotArg, zoneArg.input, zoneArg);
    }
    return this.sourceLabel(snapshotArg, zoneArg.input, zoneArg);
  }

  private static mediaArtist(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): string | undefined {
    if (this.isNetusbZone(snapshotArg, zoneArg)) {
      return snapshotArg.netusb?.artist;
    }
    if (zoneArg.input === 'tuner') {
      return snapshotArg.tuner?.rds?.radio_text_b || snapshotArg.tuner?.band;
    }
    return undefined;
  }

  private static isNetusbZone(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): boolean {
    return Boolean(snapshotArg.netusb?.input && snapshotArg.netusb.input === zoneArg.input);
  }

  private static deviceName(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): string {
    const receiver = this.receiverName(snapshotArg);
    if (zoneArg.zone === 'main') {
      return receiver;
    }
    return `${receiver} ${zoneArg.name || this.titleize(zoneArg.zone)}`;
  }

  private static receiverName(snapshotArg: IYamahaMusiccastSnapshot): string {
    return snapshotArg.networkStatus?.network_name || snapshotArg.deviceInfo.model_name || 'Yamaha MusicCast';
  }

  private static mainDeviceId(snapshotArg: IYamahaMusiccastSnapshot): string {
    return this.deviceId(snapshotArg, { zone: 'main' });
  }

  private static deviceId(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: Pick<IYamahaMusiccastZoneState, 'zone'>): string {
    const suffix = zoneArg.zone === 'main' ? '' : `.${this.slug(zoneArg.zone)}`;
    return `yamaha_musiccast.player.${this.uniqueBase(snapshotArg)}${suffix}`;
  }

  private static entityBase(snapshotArg: IYamahaMusiccastSnapshot, zoneArg: IYamahaMusiccastZoneState): string {
    const suffix = zoneArg.zone === 'main' ? '' : `_${this.slug(zoneArg.zone)}`;
    return `${this.slug(this.receiverName(snapshotArg))}${suffix}`;
  }

  private static uniqueBase(snapshotArg: IYamahaMusiccastSnapshot): string {
    return this.slug(snapshotArg.deviceInfo.system_id || snapshotArg.deviceInfo.device_id || snapshotArg.deviceInfo.serial_number || snapshotArg.deviceInfo.model_name || this.receiverName(snapshotArg));
  }

  private static range(zoneArg: IYamahaMusiccastZoneState, idArg: string): IYamahaMusiccastRangeStep | undefined {
    return zoneArg.rangeStep?.find((rangeArg) => rangeArg.id === idArg);
  }

  private static unitForCapability(capabilityArg: IYamahaMusiccastCapability): string | undefined {
    return capabilityArg.id.includes('volume') ? 'level' : undefined;
  }

  private static deviceStateValue(valueArg: unknown): plugins.shxInterfaces.data.TDeviceStateValue {
    if (typeof valueArg === 'string' || typeof valueArg === 'number' || typeof valueArg === 'boolean' || valueArg === null) {
      return valueArg;
    }
    return null;
  }

  private static titleize(valueArg: string): string {
    return valueArg.replace(/_/g, ' ').replace(/\b\w/g, (matchArg) => matchArg.toUpperCase());
  }

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