import type { FC } from '../../../lib/teact/teact';
import React, {
  memo, useCallback, useEffect, useMemo, useRef,
} from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import '../../../global/actions/calls';

import type { ApiPhoneCall, ApiUser } from '../../../api/types';

import {
  IS_ANDROID,
  IS_IOS,
  IS_REQUEST_FULLSCREEN_SUPPORTED,
  IS_SINGLE_COLUMN_LAYOUT,
} from '../../../util/environment';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
import buildClassName from '../../../util/buildClassName';
import { selectPhoneCallUser } from '../../../global/selectors/calls';
import useLang from '../../../hooks/useLang';
import renderText from '../../common/helpers/renderText';
import useFlag from '../../../hooks/useFlag';
import { formatMediaDuration } from '../../../util/dateFormat';
import {
  getStreams, IS_SCREENSHARE_SUPPORTED, switchCameraInputP2p, toggleStreamP2p,
} from '../../../lib/secret-sauce';
import useInterval from '../../../hooks/useInterval';
import useForceUpdate from '../../../hooks/useForceUpdate';

import Modal from '../../ui/Modal';
import Avatar from '../../common/Avatar';
import Button from '../../ui/Button';
import PhoneCallButton from './PhoneCallButton';
import AnimatedIcon from '../../common/AnimatedIcon';

import styles from './PhoneCall.module.scss';

type StateProps = {
  user?: ApiUser;
  phoneCall?: ApiPhoneCall;
  isOutgoing: boolean;
  isCallPanelVisible?: boolean;
};

const PhoneCall: FC<StateProps> = ({
  user,
  isOutgoing,
  phoneCall,
  isCallPanelVisible,
}) => {
  const lang = useLang();
  const {
    hangUp, acceptCall, playGroupCallSound, toggleGroupCallPanel, connectToActivePhoneCall,
  } = getActions();
  // eslint-disable-next-line no-null/no-null
  const containerRef = useRef<HTMLDivElement>(null);

  const [isFullscreen, openFullscreen, closeFullscreen] = useFlag();

  const toggleFullscreen = useCallback(() => {
    if (isFullscreen) {
      closeFullscreen();
    } else {
      openFullscreen();
    }
  }, [closeFullscreen, isFullscreen, openFullscreen]);

  const handleToggleFullscreen = useCallback(() => {
    if (!containerRef.current) return;

    if (isFullscreen) {
      document.exitFullscreen().then(closeFullscreen);
    } else {
      containerRef.current.requestFullscreen().then(openFullscreen);
    }
  }, [closeFullscreen, isFullscreen, openFullscreen]);

  useEffect(() => {
    if (!IS_REQUEST_FULLSCREEN_SUPPORTED) return undefined;
    const container = containerRef.current;
    if (!container) return undefined;

    container.addEventListener('fullscreenchange', toggleFullscreen);

    return () => {
      container.removeEventListener('fullscreenchange', toggleFullscreen);
    };
  }, [toggleFullscreen]);

  const handleClose = useCallback(() => {
    toggleGroupCallPanel();
    if (isFullscreen) {
      closeFullscreen();
    }
  }, [closeFullscreen, isFullscreen, toggleGroupCallPanel]);

  const isDiscarded = phoneCall?.state === 'discarded';
  const isBusy = phoneCall?.reason === 'busy';

  const isIncomingRequested = phoneCall?.state === 'requested' && !isOutgoing;
  const isOutgoingRequested = (phoneCall?.state === 'requested' || phoneCall?.state === 'waiting') && isOutgoing;
  const isActive = phoneCall?.state === 'active';
  const isConnected = phoneCall?.isConnected;

  useEffect(() => {
    if (isIncomingRequested) {
      playGroupCallSound({ sound: 'incoming' });
    } else if (isBusy) {
      playGroupCallSound({ sound: 'busy' });
    } else if (isDiscarded) {
      playGroupCallSound({ sound: 'end' });
    } else if (isOutgoingRequested) {
      playGroupCallSound({ sound: 'ringing' });
    } else if (isConnected) {
      playGroupCallSound({ sound: 'connect' });
    }
  }, [isBusy, isDiscarded, isIncomingRequested, isOutgoingRequested, isConnected, playGroupCallSound]);

  const [isHangingUp, startHangingUp, stopHangingUp] = useFlag();
  const handleHangUp = useCallback(() => {
    startHangingUp();
    hangUp();
  }, [hangUp, startHangingUp]);

  useEffect(() => {
    if (phoneCall?.id) {
      stopHangingUp();
    } else {
      connectToActivePhoneCall();
    }
  }, [connectToActivePhoneCall, phoneCall?.id, stopHangingUp]);

  const forceUpdate = useForceUpdate();

  useInterval(() => {
    forceUpdate();
  }, isConnected ? 1000 : undefined);

  const callStatus = useMemo(() => {
    const state = phoneCall?.state;
    if (isHangingUp) {
      return lang('lng_call_status_hanging');
    }
    if (isBusy) return 'busy';
    if (state === 'requesting') {
      return lang('lng_call_status_requesting');
    } else if (state === 'requested') {
      return isOutgoing ? lang('lng_call_status_ringing') : lang('lng_call_status_incoming');
    } else if (state === 'waiting') {
      return lang('lng_call_status_waiting');
    } else if (state === 'active' && isConnected) {
      return undefined;
    } else {
      return lang('lng_call_status_exchanging');
    }
  }, [isBusy, isConnected, isHangingUp, isOutgoing, lang, phoneCall?.state]);

  const hasVideo = phoneCall?.videoState === 'active';
  const hasPresentation = phoneCall?.screencastState === 'active';

  const streams = getStreams();
  const hasOwnAudio = streams?.ownAudio?.getTracks()[0].enabled;
  const hasOwnPresentation = streams?.ownPresentation?.getTracks()[0].enabled;
  const hasOwnVideo = streams?.ownVideo?.getTracks()[0].enabled;

  const [isHidingPresentation, startHidingPresentation, stopHidingPresentation] = useFlag();
  const [isHidingVideo, startHidingVideo, stopHidingVideo] = useFlag();

  const handleTogglePresentation = useCallback(() => {
    if (hasOwnPresentation) {
      startHidingPresentation();
    }
    if (hasOwnVideo) {
      startHidingVideo();
    }
    setTimeout(async () => {
      await toggleStreamP2p('presentation');
      stopHidingPresentation();
      stopHidingVideo();
    }, 250);
  }, [
    hasOwnPresentation, hasOwnVideo, startHidingPresentation, startHidingVideo, stopHidingPresentation, stopHidingVideo,
  ]);

  const handleToggleVideo = useCallback(() => {
    if (hasOwnVideo) {
      startHidingVideo();
    }
    if (hasOwnPresentation) {
      startHidingPresentation();
    }
    setTimeout(async () => {
      await toggleStreamP2p('video');
      stopHidingPresentation();
      stopHidingVideo();
    }, 250);
  }, [
    hasOwnPresentation, hasOwnVideo, startHidingPresentation, startHidingVideo, stopHidingPresentation, stopHidingVideo,
  ]);

  const handleToggleAudio = useCallback(() => {
    void toggleStreamP2p('audio');
  }, []);

  const [isEmojiOpen, openEmoji, closeEmoji] = useFlag();

  const [isFlipping, startFlipping, stopFlipping] = useFlag();

  const handleFlipCamera = useCallback(() => {
    startFlipping();
    switchCameraInputP2p();
    setTimeout(stopFlipping, 250);
  }, [startFlipping, stopFlipping]);

  const timeElapsed = phoneCall?.startDate && (Number(new Date()) / 1000 - phoneCall.startDate);

  useEffect(() => {
    if (phoneCall?.state === 'discarded') {
      setTimeout(hangUp, 250);
    }
  }, [hangUp, phoneCall?.reason, phoneCall?.state]);

  return (
    <Modal
      isOpen={phoneCall && phoneCall?.state !== 'discarded' && !isCallPanelVisible}
      onClose={handleClose}
      className={buildClassName(
        styles.root,
        IS_SINGLE_COLUMN_LAYOUT && styles.singleColumn,
      )}
      dialogRef={containerRef}
    >
      <Avatar user={user} size="jumbo" className={hasVideo || hasPresentation ? styles.blurred : ''} />
      {phoneCall?.screencastState === 'active' && streams?.presentation
        && <video className={styles.mainVideo} muted autoPlay playsInline srcObject={streams.presentation} />}
      {phoneCall?.videoState === 'active' && streams?.video
        && <video className={styles.mainVideo} muted autoPlay playsInline srcObject={streams.video} />}
      <video
        className={buildClassName(
          styles.secondVideo,
          !isHidingPresentation && hasOwnPresentation && styles.visible,
          isFullscreen && styles.fullscreen,
        )}
        muted
        autoPlay
        playsInline
        srcObject={streams?.ownPresentation}
      />
      <video
        className={buildClassName(
          styles.secondVideo,
          !isHidingVideo && hasOwnVideo && styles.visible,
          isFullscreen && styles.fullscreen,
        )}
        muted
        autoPlay
        playsInline
        srcObject={streams?.ownVideo}
      />
      <div className={styles.header}>
        {IS_REQUEST_FULLSCREEN_SUPPORTED && (
          <Button
            round
            size="smaller"
            color="translucent"
            onClick={handleToggleFullscreen}
            ariaLabel={lang(isFullscreen ? 'AccExitFullscreen' : 'AccSwitchToFullscreen')}
          >
            <i className={isFullscreen ? 'icon-smallscreen' : 'icon-fullscreen'} />
          </Button>
        )}

        <Button
          round
          size="smaller"
          color="translucent"
          onClick={handleClose}
          className={styles.closeButton}
        >
          <i className="icon-close" />
        </Button>
      </div>
      <div
        className={buildClassName(styles.emojisBackdrop, isEmojiOpen && styles.open)}
        onClick={!isEmojiOpen ? openEmoji : closeEmoji}
      >
        <div className={buildClassName(styles.emojis, isEmojiOpen && styles.open)}>
          {phoneCall?.isConnected && phoneCall?.emojis && renderText(phoneCall.emojis, ['emoji'])}
        </div>
        <div className={buildClassName(styles.emojiTooltip, isEmojiOpen && styles.open)}>
          {lang('CallEmojiKeyTooltip', user?.firstName).replace('%%', '%')}
        </div>
      </div>
      <div className={styles.userInfo}>
        <h1>{user?.firstName}</h1>
        <span className={styles.status}>{callStatus || formatMediaDuration(timeElapsed || 0)}</span>
      </div>
      <div className={styles.buttons}>
        <PhoneCallButton
          onClick={handleToggleAudio}
          icon="microphone"
          isDisabled={!isActive}
          isActive={hasOwnAudio}
          label={lang(hasOwnAudio ? 'lng_call_mute_audio' : 'lng_call_unmute_audio')}
        />
        <PhoneCallButton
          onClick={handleToggleVideo}
          icon="video"
          isDisabled={!isActive}
          isActive={hasOwnVideo}
          label={lang(hasOwnVideo ? 'lng_call_stop_video' : 'lng_call_start_video')}
        />
        {hasOwnVideo && (IS_ANDROID || IS_IOS) && (
          <PhoneCallButton
            onClick={handleFlipCamera}
            customIcon={(
              <AnimatedIcon
                tgsUrl={LOCAL_TGS_URLS.CameraFlip}
                playSegment={!isFlipping ? [0, 1] : [0, 10]}
                size={32}
              />
            )}
            isDisabled={!isActive}
            label={lang('VoipFlip')}
          />
        )}
        {IS_SCREENSHARE_SUPPORTED && (
          <PhoneCallButton
            onClick={handleTogglePresentation}
            icon="share-screen"
            isDisabled={!isActive}
            isActive={hasOwnPresentation}
            label={lang('lng_call_screencast')}
          />
        )}
        {isIncomingRequested && (
          <PhoneCallButton
            onClick={acceptCall}
            icon="phone-discard"
            isDisabled={isDiscarded}
            label={lang('lng_call_accept')}
            className={styles.accept}
            iconClassName={styles.acceptIcon}
          />
        )}
        <PhoneCallButton
          onClick={handleHangUp}
          icon="phone-discard"
          isDisabled={isDiscarded}
          label={lang(isIncomingRequested ? 'lng_call_decline' : 'lng_call_end_call')}
          className={styles.leave}
        />
      </div>
    </Modal>
  );
};

export default memo(withGlobal(
  (global): StateProps => {
    const { phoneCall, currentUserId } = global;

    return {
      isCallPanelVisible: Boolean(global.isCallPanelVisible),
      user: selectPhoneCallUser(global),
      isOutgoing: phoneCall?.adminId === currentUserId,
      phoneCall,
    };
  },
)(PhoneCall));
