import {
  Component,
  ElementRef,
  HostListener,
  OnDestroy,
  ViewChild,
  computed,
  input,
  output,
  signal
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideDownload,
  lucideMaximize,
  lucideMinimize,
  lucidePause,
  lucidePlay,
  lucideVolume2,
  lucideVolumeX
} from '@ng-icons/lucide';

@Component({
  selector: 'app-chat-video-player',
  standalone: true,
  imports: [CommonModule, NgIcon],
  viewProviders: [
    provideIcons({
      lucideDownload,
      lucideMaximize,
      lucideMinimize,
      lucidePause,
      lucidePlay,
      lucideVolume2,
      lucideVolumeX
    })
  ],
  templateUrl: './chat-video-player.component.html',
  styleUrl: './chat-video-player.component.scss'
})
/* eslint-disable @typescript-eslint/member-ordering */
export class ChatVideoPlayerComponent implements OnDestroy {
  src = input.required<string>();
  filename = input.required<string>();
  sizeLabel = input<string>('');
  downloadRequested = output<undefined>();

  private readonly SINGLE_CLICK_DELAY_MS = 300;
  private readonly FULLSCREEN_IDLE_MS = 2200;

  @ViewChild('playerRoot') playerRoot?: ElementRef<HTMLDivElement>;
  @ViewChild('videoEl') videoRef?: ElementRef<HTMLVideoElement>;

  isPlaying = signal(false);
  isMuted = signal(false);
  isFullscreen = signal(false);
  controlsVisible = signal(true);
  currentTimeSeconds = signal(0);
  durationSeconds = signal(0);
  volumePercent = signal(100);
  private lastNonZeroVolume = signal(100);
  private singleClickTimer: ReturnType<typeof setTimeout> | null = null;
  private controlsHideTimer: ReturnType<typeof setTimeout> | null = null;

  progressPercent = computed(() => {
    const duration = this.durationSeconds();

    if (duration <= 0)
      return 0;

    return (this.currentTimeSeconds() / duration) * 100;
  });
  seekTrackBackground = computed(() => {
    const progress = Math.max(0, Math.min(100, this.progressPercent()));

    return this.buildSliderBackground(progress);
  });
  volumeTrackBackground = computed(() => {
    const volume = Math.max(0, Math.min(100, this.isMuted() ? 0 : this.volumePercent()));

    return this.buildSliderBackground(volume);
  });

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

    this.isFullscreen.set(isFullscreen);

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

    this.controlsVisible.set(true);
    this.clearControlsHideTimer();
  }

  ngOnDestroy(): void {
    this.clearControlsHideTimer();
    this.clearSingleClickTimer();
  }

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

    this.revealControlsTemporarily();
  }

  onVideoClick(): void {
    this.clearSingleClickTimer();
    this.revealControlsTemporarily();
    this.singleClickTimer = setTimeout(() => {
      this.singleClickTimer = null;
      this.togglePlayback();
    }, this.SINGLE_CLICK_DELAY_MS);
  }

  onVideoDoubleClick(event: MouseEvent): void {
    event.preventDefault();
    event.stopPropagation();
    this.clearSingleClickTimer();
    void this.toggleFullscreen();
  }

  onOverlayPlayClick(event: MouseEvent): void {
    event.stopPropagation();
    this.togglePlayback();
  }

  togglePlayback(): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    if (video.paused || video.ended) {
      void video.play().catch(() => {
        this.isPlaying.set(false);
      });

      return;
    }

    video.pause();
    this.revealControlsTemporarily();
  }

  onLoadedMetadata(): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    this.durationSeconds.set(Number.isFinite(video.duration) ? video.duration : 0);
    this.currentTimeSeconds.set(video.currentTime || 0);
  }

  onTimeUpdate(): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    this.currentTimeSeconds.set(video.currentTime || 0);
  }

  onPlay(): void {
    this.isPlaying.set(true);
    this.revealControlsTemporarily();
  }

  onPause(): void {
    this.isPlaying.set(false);
    this.revealControlsTemporarily();
  }

  onSeek(event: Event): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    const nextTime = Number((event.target as HTMLInputElement).value);

    if (!Number.isFinite(nextTime))
      return;

    video.currentTime = nextTime;
    this.currentTimeSeconds.set(nextTime);
    this.revealControlsTemporarily();
  }

  onVolumeInput(event: Event): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    const nextVolume = Math.max(0, Math.min(100, Number((event.target as HTMLInputElement).value)));

    video.volume = nextVolume / 100;
    video.muted = nextVolume === 0;

    if (nextVolume > 0)
      this.lastNonZeroVolume.set(nextVolume);

    this.volumePercent.set(nextVolume);
    this.isMuted.set(video.muted);
    this.revealControlsTemporarily();
  }

  onVolumeChange(): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    this.isMuted.set(video.muted || video.volume === 0);

    if (!video.muted && video.volume > 0) {
      const volume = Math.round(video.volume * 100);

      this.volumePercent.set(volume);
      this.lastNonZeroVolume.set(volume);
    }

    this.revealControlsTemporarily();
  }

  toggleMute(): void {
    const video = this.videoRef?.nativeElement;

    if (!video)
      return;

    if (video.muted || video.volume === 0) {
      const restoredVolume = Math.max(this.lastNonZeroVolume(), 1);

      video.muted = false;
      video.volume = restoredVolume / 100;
      this.volumePercent.set(restoredVolume);
      this.isMuted.set(false);
      this.revealControlsTemporarily();
      return;
    }

    video.muted = true;
    this.isMuted.set(true);
    this.revealControlsTemporarily();
  }

  async toggleFullscreen(): Promise<void> {
    const player = this.playerRoot?.nativeElement;

    if (!player)
      return;

    if (document.fullscreenElement === player) {
      await document.exitFullscreen().catch(() => {});
      return;
    }

    await player.requestFullscreen?.().catch(() => {});
  }

  requestDownload(): void {
    this.downloadRequested.emit(undefined);
    this.revealControlsTemporarily();
  }

  private buildSliderBackground(fillPercent: number): string {
    return [
      'linear-gradient(90deg, ',
      'hsl(var(--primary)) 0%, ',
      `hsl(var(--primary)) ${fillPercent}%, `,
      `hsl(var(--secondary)) ${fillPercent}%, `,
      'hsl(var(--secondary)) 100%)'
    ].join('');
  }

  private revealControlsTemporarily(): void {
    if (!this.isFullscreen()) {
      this.controlsVisible.set(true);
      return;
    }

    this.controlsVisible.set(true);
    this.clearControlsHideTimer();
    this.controlsHideTimer = setTimeout(() => {
      this.controlsVisible.set(false);
    }, this.FULLSCREEN_IDLE_MS);
  }

  private clearControlsHideTimer(): void {
    if (this.controlsHideTimer) {
      clearTimeout(this.controlsHideTimer);
      this.controlsHideTimer = null;
    }
  }

  private clearSingleClickTimer(): void {
    if (this.singleClickTimer) {
      clearTimeout(this.singleClickTimer);
      this.singleClickTimer = null;
    }
  }

  formatTime(seconds: number): string {
    if (!Number.isFinite(seconds) || seconds < 0)
      return '0:00';

    const totalSeconds = Math.floor(seconds);
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const remainingSeconds = totalSeconds % 60;

    if (hours > 0) {
      return `${hours}:${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
    }

    return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
  }
}
