import { StreamVideoParticipant, VisibilityState } from '../types';
import { combineComparators, conditional } from './comparator';
import { ParticipantSource } from '../gen/video/sfu/models/models';
import {
  dominantSpeaker,
  pinned,
  publishingAudio,
  publishingVideo,
  reactionType,
  role,
  screenSharing,
  speaking,
  withParticipantSource,
} from './participants';

// a comparator decorator which applies the decorated comparator only if the
// participant is invisible.
// This ensures stable sorting when all participants are visible.
const ifInvisibleBy = conditional(
  (a: StreamVideoParticipant, b: StreamVideoParticipant) =>
    a.viewportVisibilityState?.videoTrack === VisibilityState.INVISIBLE ||
    b.viewportVisibilityState?.videoTrack === VisibilityState.INVISIBLE,
);

/**
 * A comparator that applies the decorated comparator when a participant is
 * either invisible or its visibility state isn't known.
 * For visible participants, it ensures stable sorting.
 */
const ifInvisibleOrUnknownBy = conditional(
  (a: StreamVideoParticipant, b: StreamVideoParticipant) =>
    a.viewportVisibilityState?.videoTrack === VisibilityState.INVISIBLE ||
    a.viewportVisibilityState?.videoTrack === VisibilityState.UNKNOWN ||
    b.viewportVisibilityState?.videoTrack === VisibilityState.INVISIBLE ||
    b.viewportVisibilityState?.videoTrack === VisibilityState.UNKNOWN,
);

/**
 * A comparator that prioritizes participants with video ingress sources.
 */
const withVideoIngressSource = withParticipantSource(
  ParticipantSource.RTMP,
  ParticipantSource.SRT,
  ParticipantSource.WHIP,
  ParticipantSource.RTSP,
);

/**
 * The default sorting preset.
 */
export const defaultSortPreset = combineComparators(
  screenSharing,
  pinned,
  ifInvisibleBy(
    combineComparators(
      dominantSpeaker,
      speaking,
      reactionType('raised-hand'),
      publishingVideo,
      publishingAudio,
    ),
  ),
);

/**
 * The sorting preset for speaker layout.
 */
export const speakerLayoutSortPreset = combineComparators(
  screenSharing,
  pinned,
  dominantSpeaker,
  ifInvisibleBy(
    combineComparators(
      speaking,
      reactionType('raised-hand'),
      withVideoIngressSource,
      publishingVideo,
      publishingAudio,
    ),
  ),
);

/**
 * The sorting preset for layouts that don't render all participants but
 * instead, render them in pages.
 */
export const paginatedLayoutSortPreset = combineComparators(
  pinned,
  ifInvisibleOrUnknownBy(
    combineComparators(
      dominantSpeaker,
      speaking,
      reactionType('raised-hand'),
      withVideoIngressSource,
      publishingVideo,
      publishingAudio,
    ),
  ),
);

/**
 * The sorting preset for livestreams and audio rooms.
 */
export const livestreamOrAudioRoomSortPreset = combineComparators(
  ifInvisibleOrUnknownBy(
    combineComparators(
      dominantSpeaker,
      speaking,
      reactionType('raised-hand'),
      withVideoIngressSource,
      publishingVideo,
      publishingAudio,
    ),
  ),
  role('admin', 'host', 'speaker'),
);
