/* eslint-disable @typescript-eslint/member-ordering */
import { CommonModule } from '@angular/common';
import {
  Component,
  ElementRef,
  effect,
  HostListener,
  inject,
  input,
  OnDestroy,
  output,
  signal,
  viewChild
} from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideMaximize,
  lucideMinimize,
  lucideMonitor,
  lucideRotateCw,
  lucideVideo,
  lucideVolume2,
  lucideVolumeX
} from '@ng-icons/lucide';

import { UserAvatarComponent } from '../../../../shared';
import { ViewportService } from '../../../../core/platform';
import { MobileAppLifecycleService, MobilePictureInPictureService } from '../../../../infrastructure/mobile';
import { VoiceWorkspacePlaybackService } from '../voice-workspace-playback.service';
import { VoiceWorkspaceStreamItem } from '../voice-workspace.models';
import { APP_TRANSLATE_IMPORTS, AppI18nService } from '../../../../core/i18n';

@Component({
  selector: 'app-voice-workspace-stream-tile',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    UserAvatarComponent,
    ...APP_TRANSLATE_IMPORTS
  ],
  viewProviders: [
    provideIcons({
      lucideMaximize,
      lucideMinimize,
      lucideMonitor,
      lucideRotateCw,
      lucideVideo,
      lucideVolume2,
      lucideVolumeX
    })
  ],
  templateUrl: './voice-workspace-stream-tile.component.html',
  host: {
    class: 'block h-full'
  }
})
export class VoiceWorkspaceStreamTileComponent implements OnDestroy {
  private readonly workspacePlayback = inject(VoiceWorkspacePlaybackService);
  private readonly viewport = inject(ViewportService);
  private readonly mobileLifecycle = inject(MobileAppLifecycleService);
  private readonly mobilePictureInPicture = inject(MobilePictureInPictureService);
  private readonly appI18n = inject(AppI18nService);
  private fullscreenHeaderHideTimeoutId: ReturnType<typeof setTimeout> | null = null;

  readonly item = input.required<VoiceWorkspaceStreamItem>();
  readonly focused = input(false);
  readonly featured = input(false);
  readonly compact = input(false);
  readonly mini = input(false);
  readonly immersive = input(false);
  readonly focusRequested = output<string>();
  readonly tileRef = viewChild<ElementRef<HTMLElement>>('tileRoot');
  readonly videoRef = viewChild<ElementRef<HTMLVideoElement>>('streamVideo');

  readonly isFullscreen = signal(false);
  readonly isMobile = this.viewport.isMobile;
  readonly showFullscreenHeader = signal(true);
  readonly volume = signal(100);
  readonly muted = signal(false);

  constructor() {
    void this.mobileLifecycle.initialize();
    this.mobileLifecycle.onAppStateChange((isActive) => {
      void this.handleAppStateChange(isActive);
    });

    effect(() => {
      const ref = this.videoRef();
      const item = this.item();

      if (!ref) {
        return;
      }

      const video = ref.nativeElement;

      if (video.srcObject !== item.stream) {
        video.srcObject = item.stream;
      }

      void video.play().catch(() => {});
    });

    effect(() => {
      this.workspacePlayback.settings();

      const item = this.item();

      if (item.isLocal || !item.hasAudio) {
        this.volume.set(0);
        this.muted.set(false);
        return;
      }

      this.volume.set(this.workspacePlayback.getUserVolume(item.peerKey));
      this.muted.set(this.workspacePlayback.isUserMuted(item.peerKey));
    });

    effect(() => {
      const ref = this.videoRef();
      const item = this.item();
      const muted = this.muted();
      const volume = this.volume();

      if (!ref) {
        return;
      }

      const video = ref.nativeElement;

      if (item.isLocal || !item.hasAudio) {
        video.muted = true;
        video.volume = 0;
        return;
      }

      video.muted = muted;
      video.volume = Math.max(0, Math.min(1, volume / 100));
      void video.play().catch(() => {});
    });

  }

  @HostListener('document:fullscreenchange')
  onFullscreenChange(): void {
    const tile = this.tileRef()?.nativeElement;
    const isFullscreen = !!tile && document.fullscreenElement === tile;

    this.isFullscreen.set(isFullscreen);

    if (isFullscreen) {
      this.revealFullscreenHeader();
      return;
    }

    this.unlockOrientation();
    this.clearFullscreenHeaderHideTimeout();
    this.showFullscreenHeader.set(true);
  }

  ngOnDestroy(): void {
    this.clearFullscreenHeaderHideTimeout();
    void this.mobilePictureInPicture.exit();

    const tile = this.tileRef()?.nativeElement;

    if (tile && document.fullscreenElement === tile) {
      void document.exitFullscreen().catch(() => {});
    }

    this.unlockOrientation();
  }

  private async handleAppStateChange(isActive: boolean): Promise<void> {
    if (isActive || !this.focused() || !this.mobilePictureInPicture.isSupported()) {
      if (isActive) {
        await this.mobilePictureInPicture.exit();
      }

      return;
    }

    const video = this.videoRef()?.nativeElement;

    if (!video || !this.item().stream) {
      return;
    }

    await this.mobilePictureInPicture.enter(video);
  }

  canToggleFullscreen(): boolean {
    return !this.mini() && !this.compact();
  }

  onTilePointerMove(): void {
    if (!this.isFullscreen()) {
      return;
    }

    this.revealFullscreenHeader();
  }

  async onTileDoubleClick(event: MouseEvent): Promise<void> {
    event.preventDefault();
    event.stopPropagation();

    await this.toggleFullscreen();
  }

  async toggleFullscreen(event?: Event): Promise<void> {
    event?.preventDefault();
    event?.stopPropagation();

    if (!this.canToggleFullscreen()) {
      return;
    }

    if (this.isFullscreen()) {
      await document.exitFullscreen().catch(() => {});
      return;
    }

    await this.enterFullscreen();
  }

  async enterLandscapeFullscreen(event?: Event): Promise<void> {
    event?.preventDefault();
    event?.stopPropagation();

    if (!this.canToggleFullscreen()) {
      return;
    }

    if (!this.isFullscreen()) {
      await this.enterFullscreen();
    }

    await this.lockLandscape();
  }

  async exitFullscreen(event?: Event): Promise<void> {
    event?.preventDefault();
    event?.stopPropagation();

    if (!this.isFullscreen()) {
      return;
    }

    await document.exitFullscreen().catch(() => {});
  }

  requestFocus(): void {
    this.focusRequested.emit(this.item().id);
  }

  toggleMuted(): void {
    const item = this.item();

    if (item.isLocal || !item.hasAudio) {
      return;
    }

    const nextMuted = !this.muted();

    this.muted.set(nextMuted);
    this.workspacePlayback.setUserMuted(item.peerKey, nextMuted);
  }

  updateVolume(event: Event): void {
    const item = this.item();

    if (item.isLocal || !item.hasAudio) {
      return;
    }

    const input = event.target as HTMLInputElement;
    const nextVolume = Math.max(0, Math.min(100, parseInt(input.value, 10) || 0));

    this.volume.set(nextVolume);
    this.workspacePlayback.setUserVolume(item.peerKey, nextVolume);

    if (nextVolume > 0 && this.muted()) {
      this.muted.set(false);
      this.workspacePlayback.setUserMuted(item.peerKey, false);
    }
  }

  displayName(): string {
    return this.item().isLocal ? this.appI18n.instant('common.you') : this.item().user.displayName;
  }

  streamIconName(): string {
    return this.item().kind === 'camera' ? 'lucideVideo' : 'lucideMonitor';
  }

  streamBadgeLabel(): string {
    return this.item().kind === 'camera'
      ? this.appI18n.instant('voice.streamTile.cameraLive')
      : this.appI18n.instant('voice.streamTile.screenShareLive');
  }

  tileAriaLabel(): string {
    const name = this.displayName();
    const badge = this.streamBadgeLabel();

    if (this.mini()) {
      return this.appI18n.instant('voice.streamTile.focusAria', { name, badge });
    }

    return this.appI18n.instant('voice.streamTile.openAria', { name, badge });
  }

  fullscreenToggleTitle(): string | null {
    if (!this.canToggleFullscreen()) {
      return null;
    }

    return this.isFullscreen()
      ? this.appI18n.instant('voice.streamTile.exitFullscreenTitle')
      : this.appI18n.instant('voice.streamTile.enterFullscreenTitle');
  }

  streamAudioToggleTitle(): string {
    return this.muted()
      ? this.appI18n.instant('voice.workspace.unmuteStreamAudio')
      : this.appI18n.instant('voice.workspace.muteStreamAudio');
  }

  volumeLabel(): string {
    if (this.muted()) {
      return this.appI18n.instant('common.off');
    }

    return `${this.volume()}%`;
  }

  compactAudioLabel(): string {
    if (this.muted()) {
      return this.appI18n.instant('common.muted');
    }

    return this.appI18n.instant('voice.streamTile.audioPercent', { volume: this.volume() });
  }

  fullscreenDescription(): string {
    if (this.item().isLocal) {
      return this.item().kind === 'camera'
        ? this.appI18n.instant('voice.streamTile.localCameraFullscreen')
        : this.appI18n.instant('voice.streamTile.localPreviewFullscreen');
    }

    return this.item().kind === 'camera'
      ? this.appI18n.instant('voice.streamTile.cameraFullscreen')
      : this.appI18n.instant('voice.streamTile.streamFullscreen');
  }

  localPreviewDescription(): string {
    return this.item().kind === 'camera'
      ? this.appI18n.instant('voice.streamTile.localCameraPreview')
      : this.appI18n.instant('voice.streamTile.localPreviewMuted');
  }

  widescreenButtonTitle(): string {
    return this.focused()
      ? this.appI18n.instant('voice.streamTile.viewingWidescreen')
      : this.appI18n.instant('voice.streamTile.viewInWidescreen');
  }

  canControlStreamAudio(): boolean {
    const item = this.item();

    return !item.isLocal && item.hasAudio;
  }

  private async enterFullscreen(): Promise<void> {
    const tile = this.tileRef()?.nativeElement;

    if (tile?.requestFullscreen) {
      await tile.requestFullscreen().catch(() => {});
      return;
    }

    const video = this.videoRef()?.nativeElement as WebKitFullscreenVideoElement | undefined;

    if (video?.webkitSupportsFullscreen && video.webkitEnterFullscreen) {
      video.webkitEnterFullscreen();
    }
  }

  private async lockLandscape(): Promise<void> {
    if (!this.isMobile()) {
      return;
    }

    const orientation = screen.orientation as LockableScreenOrientation | undefined;

    await orientation?.lock?.('landscape').catch(() => {});
  }

  private unlockOrientation(): void {
    screen.orientation?.unlock?.();
  }

  private scheduleFullscreenHeaderHide(): void {
    this.clearFullscreenHeaderHideTimeout();

    this.fullscreenHeaderHideTimeoutId = setTimeout(() => {
      this.showFullscreenHeader.set(false);
      this.fullscreenHeaderHideTimeoutId = null;
    }, 2200);
  }

  private revealFullscreenHeader(): void {
    this.showFullscreenHeader.set(true);
    this.scheduleFullscreenHeaderHide();
  }

  private clearFullscreenHeaderHideTimeout(): void {
    if (this.fullscreenHeaderHideTimeoutId === null) {
      return;
    }

    clearTimeout(this.fullscreenHeaderHideTimeoutId);
    this.fullscreenHeaderHideTimeoutId = null;
  }
}

interface WebKitFullscreenVideoElement extends HTMLVideoElement {
  webkitEnterFullscreen?: () => void;
  webkitSupportsFullscreen?: boolean;
}

interface LockableScreenOrientation extends ScreenOrientation {
  lock?: (orientation: 'landscape') => Promise<void>;
}
