import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useMedia } from 'react-use';
import { HMSHLSPlayer } from '@100mslive/hls-player';
import { JoinForm_JoinBtnType } from '@100mslive/types-prebuilt/elements/join_form';
import {
  HMSPeer,
  HMSRecording,
  parsedUserAgent,
  selectAvailableRoleNames,
  selectIsAllowedToPublish,
  selectIsConnectedToRoom,
  selectLocalPeerRole,
  selectPeerCount,
  selectPeerMetadata,
  selectPeers,
  selectPeersByRoles,
  selectRecordingState,
  selectRemotePeers,
  selectRolesMap,
  useHMSActions,
  useHMSStore,
  useHMSVanillaStore,
} from '@100mslive/react-sdk';
// @ts-ignore: No implicit any
import { ToastManager } from '../components/Toast/ToastManager';
import { config } from '../../Theme';
import { useRoomLayout } from '../provider/roomLayoutProvider';
// @ts-ignore
import { useSetAppDataByKey } from '../components/AppData/useUISettings';
import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
// @ts-ignore: No implicit any
import { isScreenshareSupported } from '../common/utils';
import { APP_DATA, CHAT_SELECTOR, RTMP_RECORD_DEFAULT_RESOLUTION } from './constants';
/**
 * Hook to execute a callback when alone in room(after a certain 5d of time)
 * @param {number} thresholdMs The threshold(in ms) after which the callback is executed,
 * starting from the instant when alone in room.
 * note: the cb is not called when another peer joins during this period.
 */
export const useWhenAloneInRoom = (thresholdMs = 5 * 60 * 1000) => {
  const isConnected = useHMSStore(selectIsConnectedToRoom);
  const peerCount = useHMSStore(selectPeerCount);
  const [aloneForLong, setAloneForLong] = useState(false);
  const cbTimeout = useRef(null);
  const alone = isConnected && peerCount === 1;

  useEffect(() => {
    if (alone && !cbTimeout.current) {
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      cbTimeout.current = setTimeout(() => {
        setAloneForLong(true);
      }, thresholdMs);
    } else if (!alone) {
      cbTimeout.current && clearTimeout(cbTimeout.current);
      cbTimeout.current = null;
      setAloneForLong(false);
    }
  }, [alone, thresholdMs]);

  useEffect(() => {
    return () => {
      if (cbTimeout.current) {
        clearTimeout(cbTimeout.current);
      }
    };
  }, []);

  return { alone, aloneForLong };
};

export const useFilteredRoles = () => {
  const { elements } = useRoomLayoutConferencingScreen();
  return elements?.chat?.roles_whitelist || [];
};

export const useDefaultChatSelection = () => {
  const { elements } = useRoomLayoutConferencingScreen();
  const roles = useFilteredRoles();
  // default is everyone for public chat
  if (elements?.chat?.public_chat_enabled) {
    return CHAT_SELECTOR.EVERYONE;
  }
  // sending first role as default
  if (roles.length > 0) {
    return roles[0];
  }
  // sending empty
  return '';
};

export const useShowStreamingUI = () => {
  const layout = useRoomLayout();
  const { join_form } = layout?.screens?.preview?.default?.elements || {};
  return join_form?.join_btn_type === JoinForm_JoinBtnType.JOIN_BTN_TYPE_JOIN_AND_GO_LIVE;
};

// The search results should not have role name matches
export const useParticipants = (params?: { metadata?: { isHandRaised?: boolean }; role?: string; search?: string }) => {
  const isConnected = useHMSStore(selectIsConnectedToRoom);
  const peerCount = useHMSStore(selectPeerCount);
  const availableRoles = useHMSStore(selectAvailableRoleNames);
  let participantList = useHMSStore(isConnected ? selectPeers : selectRemotePeers);
  const rolesWithParticipants = Array.from(new Set(participantList.map(peer => peer.roleName)));
  const vanillaStore = useHMSVanillaStore();
  if (params?.metadata?.isHandRaised) {
    participantList = participantList.filter(peer => {
      return vanillaStore.getState(selectPeerMetadata(peer.id)).isHandRaised;
    });
  }
  if (params?.role && availableRoles.includes(params.role)) {
    participantList = participantList.filter(peer => peer.roleName === params.role);
  }
  if (params?.search) {
    const search = params.search;
    // Removed peer.roleName?.toLowerCase().includes(search)
    participantList = participantList.filter(peer => peer.name.toLowerCase().includes(search));
  }
  return { participants: participantList, isConnected, peerCount, rolesWithParticipants };
};

export const useIsLandscape = () => {
  const isMobile = parsedUserAgent.getDevice().type === 'mobile';
  const isLandscape = useMedia(config.media.ls);
  return isMobile && isLandscape;
};

export const useLandscapeHLSStream = () => {
  const isLandscape = useIsLandscape();
  const { screenType } = useRoomLayoutConferencingScreen();
  return isLandscape && screenType === 'hls_live_streaming';
};

export const useMobileHLSStream = () => {
  const isMobile = useMedia(config.media.md);
  const { screenType } = useRoomLayoutConferencingScreen();
  return isMobile && screenType === 'hls_live_streaming';
};

export const useKeyboardHandler = (isPaused: boolean, hlsPlayer: HMSHLSPlayer) => {
  const handleKeyEvent = useCallback(
    async (event: KeyboardEvent) => {
      switch (event.key) {
        case ' ':
          if (isPaused) {
            await hlsPlayer?.play();
          } else {
            hlsPlayer?.pause();
          }
          break;
        case 'ArrowRight':
          hlsPlayer?.seekTo(hlsPlayer?.getVideoElement().currentTime + 10);
          break;
        case 'ArrowLeft':
          hlsPlayer?.seekTo(hlsPlayer?.getVideoElement().currentTime - 10);
          break;
      }
    },
    [hlsPlayer, isPaused],
  );

  return handleKeyEvent;
};
export interface RTMPRecordingResolution {
  width: number;
  height: number;
}
export const useRecordingHandler = () => {
  const hmsActions = useHMSActions();
  const recordingState: HMSRecording = useHMSStore(selectRecordingState);
  const [isRecordingLoading, setIsRecordingLoading] = useState(false);
  const [recordingStarted, setRecordingState] = useSetAppDataByKey(APP_DATA.recordingStarted);
  useEffect(() => {
    if (recordingState.browser.error && recordingStarted) {
      setRecordingState(false);
    }
  }, [recordingStarted, recordingState.browser.error, setRecordingState]);
  const startRecording = useCallback(
    async (resolution: RTMPRecordingResolution | null = null) => {
      try {
        setRecordingState(true);
        setIsRecordingLoading(true);
        await hmsActions.startRTMPOrRecording({
          resolution: getResolution(resolution),
          record: true,
        });
      } catch (error) {
        const err = error as Error;
        if (err.message.includes('stream already running')) {
          ToastManager.addToast({
            title: 'Recording already running',
            variant: 'error',
          });
        } else {
          ToastManager.addToast({
            title: err.message,
            variant: 'error',
          });
        }
        setRecordingState(false);
      }
      setIsRecordingLoading(false);
    },
    [hmsActions, setRecordingState],
  );
  return {
    recordingStarted,
    startRecording,
    isRecordingLoading,
  };
};

export function getResolution(
  recordingResolution: RTMPRecordingResolution | null,
): RTMPRecordingResolution | undefined {
  if (!recordingResolution) {
    return undefined;
  }
  const resolution: RTMPRecordingResolution = RTMP_RECORD_DEFAULT_RESOLUTION;
  if (recordingResolution.width) {
    resolution.width = recordingResolution.width;
  }
  if (recordingResolution.height) {
    resolution.height = recordingResolution.height;
  }
  return resolution;
}

export interface WaitingRoomInfo {
  isNotAllowedToPublish: boolean;
  isScreenOnlyPublishParams: boolean;
  hasSubscribedRolePublishing: boolean;
}
export function useWaitingRoomInfo(): WaitingRoomInfo {
  const localPeerRole = useHMSStore(selectLocalPeerRole);
  const { video, audio, screen } = useHMSStore(selectIsAllowedToPublish);
  const isScreenShareAllowed = isScreenshareSupported();
  const roles = useHMSStore(selectRolesMap);
  const peersByRoles = useHMSStore(selectPeersByRoles(localPeerRole?.subscribeParams.subscribeToRoles || []));
  // show no publish as screenshare in mweb is not possible
  const isNotAllowedToPublish = !(video || audio || (screen && isScreenShareAllowed));
  const isScreenOnlyPublishParams: boolean = screen && !(video || audio);
  const hasSubscribedRolePublishing: boolean = useMemo(() => {
    return peersByRoles.some((peer: HMSPeer) => {
      if (peer.roleName && roles[peer.roleName] && !peer.isLocal) {
        return !!roles[peer.roleName].publishParams?.allowed.length;
      }
      return false;
    });
  }, [peersByRoles, roles]);

  return {
    isNotAllowedToPublish,
    isScreenOnlyPublishParams,
    hasSubscribedRolePublishing,
  };
}
