/* eslint-disable @typescript-eslint/member-ordering */
import {
  Component,
  inject,
  computed,
  signal
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideMic,
  lucideHeadphones,
  lucideAudioLines,
  lucideMonitor,
  lucideCpu
} from '@ng-icons/lucide';

import type { DesktopSettingsSnapshot } from '../../../../core/platform/electron/electron-api.models';
import { ElectronBridgeService } from '../../../../core/platform/electron/electron-bridge.service';
import { VoiceConnectionFacade } from '../../../../domains/voice-connection';
import { SCREEN_SHARE_QUALITY_OPTIONS, ScreenShareQuality } from '../../../../domains/screen-share';
import { loadVoiceSettingsFromStorage, saveVoiceSettingsToStorage } from '../../../../domains/voice-session';
import { VoicePlaybackService } from '../../../../domains/voice-connection';
import { NotificationAudioService, AppSound } from '../../../../core/services/notification-audio.service';
import { PlatformService } from '../../../../core/platform';

interface AudioDevice {
  deviceId: string;
  label: string;
}

@Component({
  selector: 'app-voice-settings',
  standalone: true,
  imports: [
    CommonModule,
    FormsModule,
    NgIcon
  ],
  viewProviders: [
    provideIcons({
      lucideMic,
      lucideHeadphones,
      lucideAudioLines,
      lucideMonitor,
      lucideCpu
    })
  ],
  templateUrl: './voice-settings.component.html'
})
export class VoiceSettingsComponent {
  private voiceConnection = inject(VoiceConnectionFacade);
  private voicePlayback = inject(VoicePlaybackService);
  private electronBridge = inject(ElectronBridgeService);
  private platform = inject(PlatformService);
  readonly audioService = inject(NotificationAudioService);
  readonly isElectron = this.platform.isElectron;
  readonly screenShareQualityOptions = SCREEN_SHARE_QUALITY_OPTIONS;

  inputDevices = signal<AudioDevice[]>([]);
  outputDevices = signal<AudioDevice[]>([]);
  selectedInputDevice = signal<string>('');
  selectedOutputDevice = signal<string>('');
  inputVolume = signal(100);
  outputVolume = signal(100);
  audioBitrate = signal(96);
  latencyProfile = signal<'low' | 'balanced' | 'high'>('balanced');
  includeSystemAudio = signal(false);
  noiseReduction = signal(true);
  screenShareQuality = signal<ScreenShareQuality>('balanced');
  askScreenShareQuality = signal(true);
  hardwareAcceleration = signal(true);
  hardwareAccelerationRestartRequired = signal(false);
  readonly selectedScreenShareQualityDescription = computed(
    () => this.screenShareQualityOptions.find((option) => option.id === this.screenShareQuality())?.description ?? ''
  );

  constructor() {
    this.loadVoiceSettings();
    this.loadAudioDevices();

    if (this.isElectron) {
      void this.loadDesktopSettings();
    }
  }

  async loadAudioDevices(): Promise<void> {
    try {
      if (!navigator.mediaDevices?.enumerateDevices)
        return;

      const devices = await navigator.mediaDevices.enumerateDevices();

      this.inputDevices.set(
        devices
          .filter((device) => device.kind === 'audioinput')
          .map((device) => ({ deviceId: device.deviceId,
            label: device.label }))
      );

      this.outputDevices.set(
        devices
          .filter((device) => device.kind === 'audiooutput')
          .map((device) => ({ deviceId: device.deviceId,
            label: device.label }))
      );
    } catch {}
  }

  loadVoiceSettings(): void {
    const settings = loadVoiceSettingsFromStorage();

    this.selectedInputDevice.set(settings.inputDevice);
    this.selectedOutputDevice.set(settings.outputDevice);
    this.inputVolume.set(settings.inputVolume);
    this.outputVolume.set(settings.outputVolume);
    this.audioBitrate.set(settings.audioBitrate);
    this.latencyProfile.set(settings.latencyProfile);
    this.includeSystemAudio.set(settings.includeSystemAudio);
    this.noiseReduction.set(settings.noiseReduction);
    this.screenShareQuality.set(settings.screenShareQuality);
    this.askScreenShareQuality.set(settings.askScreenShareQuality);

    if (this.noiseReduction() !== this.voiceConnection.isNoiseReductionEnabled()) {
      this.voiceConnection.toggleNoiseReduction(this.noiseReduction());
    }

    // Apply persisted volume levels to the live audio pipelines
    this.voiceConnection.setInputVolume(this.inputVolume() / 100);
    this.voiceConnection.setOutputVolume(this.outputVolume() / 100);
    this.voicePlayback.updateOutputVolume(this.outputVolume() / 100);
  }

  saveVoiceSettings(): void {
    saveVoiceSettingsToStorage({
      inputDevice: this.selectedInputDevice(),
      outputDevice: this.selectedOutputDevice(),
      inputVolume: this.inputVolume(),
      outputVolume: this.outputVolume(),
      audioBitrate: this.audioBitrate(),
      latencyProfile: this.latencyProfile(),
      includeSystemAudio: this.includeSystemAudio(),
      noiseReduction: this.noiseReduction(),
      screenShareQuality: this.screenShareQuality(),
      askScreenShareQuality: this.askScreenShareQuality()
    });
  }

  onInputDeviceChange(event: Event): void {
    const select = event.target as HTMLSelectElement;

    this.selectedInputDevice.set(select.value);
    this.saveVoiceSettings();
  }

  onOutputDeviceChange(event: Event): void {
    const select = event.target as HTMLSelectElement;

    this.selectedOutputDevice.set(select.value);
    this.voiceConnection.setOutputVolume(this.outputVolume() / 100);
    this.saveVoiceSettings();
  }

  onInputVolumeChange(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.inputVolume.set(parseInt(input.value, 10));
    this.voiceConnection.setInputVolume(this.inputVolume() / 100);
    this.saveVoiceSettings();
  }

  onOutputVolumeChange(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.outputVolume.set(parseInt(input.value, 10));
    this.voiceConnection.setOutputVolume(this.outputVolume() / 100);
    this.voicePlayback.updateOutputVolume(this.outputVolume() / 100);
    this.saveVoiceSettings();
  }

  onLatencyProfileChange(event: Event): void {
    const select = event.target as HTMLSelectElement;
    const profile = select.value as 'low' | 'balanced' | 'high';

    this.latencyProfile.set(profile);
    this.voiceConnection.setLatencyProfile(profile);
    this.saveVoiceSettings();
  }

  onAudioBitrateChange(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.audioBitrate.set(parseInt(input.value, 10));
    this.voiceConnection.setAudioBitrate(this.audioBitrate());
    this.saveVoiceSettings();
  }

  onIncludeSystemAudioChange(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.includeSystemAudio.set(!!input.checked);
    this.saveVoiceSettings();
  }

  onScreenShareQualityChange(event: Event): void {
    const select = event.target as HTMLSelectElement;

    this.screenShareQuality.set(select.value as ScreenShareQuality);
    this.saveVoiceSettings();
  }

  onAskScreenShareQualityChange(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.askScreenShareQuality.set(!!input.checked);
    this.saveVoiceSettings();
  }

  async onNoiseReductionChange(): Promise<void> {
    this.noiseReduction.update((currentValue) => !currentValue);
    await this.voiceConnection.toggleNoiseReduction(this.noiseReduction());
    this.saveVoiceSettings();
  }

  onNotificationVolumeChange(event: Event): void {
    const input = event.target as HTMLInputElement;

    this.audioService.setNotificationVolume(parseFloat(input.value));
  }

  previewNotificationSound(): void {
    this.audioService.play(AppSound.Notification);
  }

  async onHardwareAccelerationChange(event: Event): Promise<void> {
    const input = event.target as HTMLInputElement;
    const enabled = !!input.checked;
    const api = this.electronBridge.getApi();

    if (!api) {
      this.hardwareAcceleration.set(enabled);
      return;
    }

    try {
      const snapshot = await api.setDesktopSettings({ hardwareAcceleration: enabled });

      this.applyDesktopSettings(snapshot);
    } catch {
      input.checked = this.hardwareAcceleration();
    }
  }

  async restartDesktopApp(): Promise<void> {
    const api = this.electronBridge.getApi();

    if (api) {
      await api.relaunchApp();
    }
  }

  private async loadDesktopSettings(): Promise<void> {
    const api = this.electronBridge.getApi();

    if (!api) {
      return;
    }

    try {
      const snapshot = await api.getDesktopSettings();

      this.applyDesktopSettings(snapshot);
    } catch {}
  }

  private applyDesktopSettings(snapshot: DesktopSettingsSnapshot): void {
    this.hardwareAcceleration.set(snapshot.hardwareAcceleration);
    this.hardwareAccelerationRestartRequired.set(snapshot.restartRequired);
  }
}
