import {
  DestroyRef,
  Injectable,
  inject
} from '@angular/core';
import { Router } from '@angular/router';

import type { CallNotificationActionIntent } from '../logic/call-notification.rules';
import { MobileAppLifecycleService } from './mobile-app-lifecycle.service';
import { MobileCallKitService } from './mobile-callkit.service';
import { MobileMediaService } from './mobile-media.service';
import { MobileNotificationsService } from './mobile-notifications.service';
import { MobilePictureInPictureService } from './mobile-picture-in-picture.service';
import { MobilePlatformService } from './mobile-platform.service';

export interface ActiveCallSessionState {
  callId: string;
  displayName: string;
  isMuted: boolean;
  focusedStreamVideo?: HTMLVideoElement | null;
}

/** Coordinates in-call notifications, background audio, and stream pop-out for mobile shells. */
@Injectable({ providedIn: 'root' })
export class MobileCallSessionService {
  private readonly destroyRef = inject(DestroyRef);
  private readonly router = inject(Router);
  private readonly mobilePlatform = inject(MobilePlatformService);
  private readonly notifications = inject(MobileNotificationsService);
  private readonly media = inject(MobileMediaService);
  private readonly pictureInPicture = inject(MobilePictureInPictureService);
  private readonly lifecycle = inject(MobileAppLifecycleService);
  private readonly callKit = inject(MobileCallKitService);

  private activeSession: ActiveCallSessionState | null = null;
  private actionHandler: ((intent: CallNotificationActionIntent, callId: string) => void) | null = null;
  private wired = false;

  initialize(): void {
    if (this.wired) {
      return;
    }

    this.wired = true;

    void this.bootstrap();

    this.destroyRef.onDestroy(() => {
      this.activeSession = null;
      this.actionHandler = null;
    });
  }

  onCallControlAction(handler: (intent: CallNotificationActionIntent, callId: string) => void): void {
    this.actionHandler = handler;
  }

  async notifyIncomingCall(displayName: string, callId: string): Promise<void> {
    if (!this.shouldHandleMobileCalls()) {
      return;
    }

    await this.notifications.showIncomingCall(displayName, callId);
  }

  async startActiveCall(session: ActiveCallSessionState): Promise<void> {
    if (!this.shouldHandleMobileCalls()) {
      return;
    }

    this.activeSession = session;
    await this.media.startBackgroundAudioSession();
    await this.callKit.startActiveCall(session.callId, session.displayName);
    await this.notifications.dismissIncomingCall(session.callId);
    await this.notifications.showActiveCall(session);
  }

  async updateActiveCall(session: ActiveCallSessionState): Promise<void> {
    if (!this.shouldHandleMobileCalls()) {
      return;
    }

    this.activeSession = session;
    await this.notifications.showActiveCall(session);
  }

  async endActiveCall(callId: string): Promise<void> {
    await this.notifications.dismissIncomingCall(callId);
    await this.notifications.dismissActiveCall(callId);
    await this.callKit.endActiveCall(callId);
    await this.media.stopBackgroundAudioSession();
    await this.pictureInPicture.exit();

    if (this.activeSession?.callId === callId) {
      this.activeSession = null;
    }
  }

  setFocusedStreamVideo(videoElement: HTMLVideoElement | null): void {
    if (!this.activeSession) {
      return;
    }

    this.activeSession = {
      ...this.activeSession,
      focusedStreamVideo: videoElement
    };
  }

  private async bootstrap(): Promise<void> {
    await this.notifications.initialize();
    await this.lifecycle.initialize();

    this.notifications.onCallAction(({ callId, intent }) => {
      void this.router.navigate(['/call', callId]);
      this.actionHandler?.(intent, callId);
    });

    this.lifecycle.onAppStateChange((isActive) => {
      void this.handleAppStateChange(isActive);
    });
  }

  private shouldHandleMobileCalls(): boolean {
    return this.mobilePlatform.isNativeMobile();
  }

  private async handleAppStateChange(isActive: boolean): Promise<void> {
    if (!this.activeSession || isActive) {
      return;
    }

    const video = this.activeSession.focusedStreamVideo;

    if (video && this.pictureInPicture.isSupported()) {
      await this.pictureInPicture.enter(video);
    }
  }
}
