/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import {
  Subject,
  filter,
  type Observable
} from 'rxjs';
import { RealtimeSessionFacade } from '../../../../core/realtime';
import { selectAllUsers } from '../../../../store/users/users.selectors';
import type { ChatEvent, User } from '../../../../shared-kernel';

@Injectable({ providedIn: 'root' })
export class PeerDeliveryService {
  private readonly webrtc = inject(RealtimeSessionFacade);
  private readonly store = inject(Store);
  private readonly users = this.store.selectSignal(selectAllUsers);
  private readonly networkRestoredSubject = new Subject<void>();

  readonly directMessageEvents$: Observable<ChatEvent> = this.webrtc.onMessageReceived.pipe(
    filter((event) => event.type === 'direct-message' || event.type === 'direct-message-status' || event.type === 'direct-message-mutation')
  );

  readonly peerConnected$ = this.webrtc.onPeerConnected;
  readonly networkRestored$ = this.networkRestoredSubject.asObservable();

  constructor() {
    this.installNetworkTestHooks();
  }

  sendViaWebRTC(recipientId: string, event: ChatEvent): boolean {
    if (this.isOfflineOverrideEnabled()) {
      return false;
    }

    const peerId = this.resolvePeerId(recipientId);

    if (!peerId) {
      return false;
    }

    this.webrtc.sendToPeer(peerId, event);
    return true;
  }

  handleAck(recipientId: string, event: ChatEvent): boolean {
    return this.sendViaWebRTC(recipientId, event);
  }

  requestUserAvatar(recipientId: string): boolean {
    return this.sendViaWebRTC(recipientId, {
      type: 'user-avatar-request',
      oderId: recipientId
    });
  }

  syncOnReconnect(onReconnect: () => void): void {
    this.peerConnected$.subscribe(() => onReconnect());
  }

  private resolvePeerId(recipientId: string): string | null {
    const connectedPeerIds = new Set(this.webrtc.getConnectedPeers());

    if (connectedPeerIds.has(recipientId)) {
      return recipientId;
    }

    const user = this.users().find((candidate: User) =>
      candidate.id === recipientId || candidate.oderId === recipientId || candidate.peerId === recipientId
    );
    const candidates = [
      user?.oderId,
      user?.peerId,
      user?.id
    ].filter((candidate): candidate is string => !!candidate);

    return candidates.find((candidate) => connectedPeerIds.has(candidate)) ?? null;
  }

  private isOfflineOverrideEnabled(): boolean {
    return typeof window !== 'undefined'
      && !!(window as Window & { metoyouDmNetworkOffline?: boolean }).metoyouDmNetworkOffline;
  }

  private installNetworkTestHooks(): void {
    if (typeof window === 'undefined') {
      return;
    }

    const testWindow = window as Window & {
      simulateOffline?: () => void;
      simulateOnline?: () => void;
      metoyouDmNetworkOffline?: boolean;
    };

    testWindow.simulateOffline = () => {
      testWindow.metoyouDmNetworkOffline = true;
    };

    testWindow.simulateOnline = () => {
      testWindow.metoyouDmNetworkOffline = false;
      this.networkRestoredSubject.next();
    };
  }
}
