/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import { createEffect } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import {
  EMPTY,
  from,
  merge,
  Observable
} from 'rxjs';
import {
  distinctUntilChanged,
  map,
  mergeMap
} from 'rxjs/operators';
import { selectCurrentUser } from '../../../store/users/users.selectors';
import {
  ChatEvent,
  CustomEmoji,
  CustomEmojiSummaryItem,
  CustomEmojiTransferManifest
} from '../../../shared-kernel';
import { RealtimeSessionFacade } from '../../../core/realtime';
import { CustomEmojiService } from './custom-emoji.service';

@Injectable()
export class CustomEmojiSyncEffects {
  private readonly customEmoji = inject(CustomEmojiService);
  private readonly store = inject(Store);
  private readonly webrtc = inject(RealtimeSessionFacade);
  private readonly incomingEvents$ = merge(
    this.webrtc.onMessageReceived,
    this.webrtc.onSignalingMessage as Observable<ChatEvent>
  );

  currentUserLoad$ = createEffect(
    () => this.store.select(selectCurrentUser).pipe(
      map((user) => user?.id ?? null),
      distinctUntilChanged(),
      mergeMap((userId) => from(this.customEmoji.loadForUser(userId)).pipe(mergeMap(() => EMPTY)))
    ),
    { dispatch: false }
  );

  peerConnectedSummary$ = createEffect(
    () => this.webrtc.onPeerConnected.pipe(
      mergeMap((peerId) => from(this.customEmoji.sendSummaryToPeer(peerId)).pipe(mergeMap(() => EMPTY)))
    ),
    { dispatch: false }
  );

  incomingCustomEmojiEvents$ = createEffect(
    () => this.incomingEvents$.pipe(
      mergeMap((event) => {
        if (event.type === 'message' || event.type === 'chat-message') {
          if (!event.fromPeerId || typeof event.message?.content !== 'string') {
            return EMPTY;
          }

          return from(this.customEmoji.requestMissingFromMessageContent(event.fromPeerId, event.message.content)).pipe(mergeMap(() => EMPTY));
        }

        if (event.type === 'chat-sync-batch' || event.type === 'chat-sync-full') {
          if (!event.fromPeerId || !Array.isArray(event.messages)) {
            return EMPTY;
          }

          return from(Promise.all(event.messages.map((message) => this.customEmoji.requestMissingFromMessageContent(
            event.fromPeerId as string,
            message.content
          )))).pipe(mergeMap(() => EMPTY));
        }

        switch (event.type) {
          case 'custom-emoji-summary': {
            if (!event.fromPeerId || !Array.isArray(event.customEmojiSummaries)) {
              return EMPTY;
            }

            const ids = this.customEmoji.missingFromSummary(event.customEmojiSummaries as CustomEmojiSummaryItem[]);

            if (ids.length === 0) {
              return EMPTY;
            }

            this.webrtc.sendToPeer(event.fromPeerId, {
              type: 'custom-emoji-request',
              ids
            });

            return EMPTY;
          }

          case 'custom-emoji-request':
            if (!event.fromPeerId || !Array.isArray(event.ids)) {
              return EMPTY;
            }

            return from(this.customEmoji.sendRequestedToPeer(event.fromPeerId, event.ids)).pipe(mergeMap(() => EMPTY));

          case 'custom-emoji-full':
            if (event.customEmoji) {
              return from(this.customEmoji.saveRemoteEmoji(event.customEmoji as CustomEmoji)).pipe(mergeMap(() => EMPTY));
            }

            if (!event.customEmojiTransfer || typeof event.total !== 'number') {
              return EMPTY;
            }

            this.customEmoji.receiveTransferStart(event.customEmojiTransfer as CustomEmojiTransferManifest, event.total);
            return EMPTY;

          case 'custom-emoji-chunk':
            if (!event.customEmojiId || typeof event.index !== 'number' || typeof event.total !== 'number' || typeof event.data !== 'string') {
              return EMPTY;
            }

            return from(this.customEmoji.receiveTransferChunk(event.customEmojiId, event.index, event.total, event.data)).pipe(mergeMap(() => EMPTY));

          default:
            return EMPTY;
        }
      })
    ),
    { dispatch: false }
  );
}
