import React from 'react'
import PropTypes from 'prop-types'
import fscreen from 'fscreen'
import styled from 'styled-components'
import {
  useThemeTokens,
  selectSystemProps,
  getTokensPropType,
  variantProp
} from '@telus-uds/components-base'
import Spinner from '../Spinner'
import { warn, htmlAttrs } from '../utils'
import VideoSplash from '../shared/VideoSplash/VideoSplash'
import MiddleControlButton from './MiddleControlButton/MiddleControlButton'
import ControlBar from './ControlBar/ControlBar'
import videoText from './videoText'

const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])

// TODO: replace with the actual spinner component from UDS
const VideoPlayerContainer = styled.div(({ videoBorder, borderColor }) => ({
  width: '100%',
  outline: 'none',
  position: 'relative',
  border: videoBorder ? `20px solid ${borderColor}` : undefined
}))

const VideoElementContainer = styled.div({
  outline: 'none',
  width: '100%',
  height: '100%',
  fontSize: 0
})

const VideoPlayer = styled.video({
  width: '100%',
  height: '100%'
})

const VideoOverlayContainer = styled.div(({ mouseInactive }) => ({
  width: '100%',
  height: '100%',
  position: 'absolute',
  cursor: mouseInactive ? 'none' : 'pointer'
}))

const VideoSplashContainer = styled.div({
  position: 'absolute',
  width: '100%',
  height: '100%',
  zIndex: 5
})

const VideoOverlayElementContainer = styled.div({
  position: 'absolute',
  zIndex: 4,
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  width: '100%',
  height: '100%'
})

const Video = ({
  analyticsTracking,
  videoTitle,
  videoBorder = false,
  simpleMode = false,
  copy,
  posterSrc,
  crossOrigin,
  defaultVolume = 100,
  beginMuted = false,
  sources,
  tracks,
  defaultDesktopQuality = 1,
  defaultMobileQuality = 1,
  tokens,
  variant = {},
  ...rest
}) => {
  const refVideoPlayer = React.useRef({})
  const refVideoPlayerContainer = React.useRef({})
  const refKeyboardInstructions = React.useRef({})

  let inactivityTimer = null

  const { borderColor, pauseIcon, playIcon, replayIcon } = useThemeTokens('Video', tokens, variant)

  const playerOptions = {
    mouseTimeout: simpleMode ? 750 : 1500, // defined in ms
    keyboardSeekIncrement: 5, // defined in s
    keyboardVolumeIncrement: 0.1, // from 0 to 1
    compactModeThreshold: 700 // in px
  }

  const [videoPlayerState, setVideoPlayerState] = React.useState({
    loadedSources: null,
    loadedTracks: null,
    videoLength: 0,
    videoCurrentTime: -1,
    videoBufferEnd: 0,
    videoUnplayed: true,
    videoIsBuffering: false,
    videoCurrentVolume: 1,
    videoIsPlaying: false,
    videoIsMuted: false,
    videoIsFullscreen: false,
    mouseInactive: false,
    videoEnded: false,
    videoQuality: defaultDesktopQuality,
    videoQualityChanged: false,
    selectedTextTrack: -1,
    mouseOnControlBar: false,
    qualityMenuOpen: false,
    captionsMenuOpen: false,
    isMobile: false,
    videoPlayerWidth: 0,
    percentageWatched: 'watched 0%'
  })

  const generateSources = (videoQuality) => {
    return sources.map((source) => {
      if (source.qualityRank === videoQuality) {
        return React.createElement('source', {
          src: source.source,
          type: source.mediaType,
          key: source.source
        })
      }

      return undefined
    })
  }

  const generateTracks = () => {
    return tracks.map((track) => {
      return React.createElement('track', {
        label: track.label,
        kind: track.kind,
        srcLang: track.language,
        src: track.source,
        default: track.isDefault,
        key: track.source
      })
    })
  }

  const refreshMedia = () => {
    const { videoUnplayed } = videoPlayerState

    // Handle mobile check
    const isMobile = navigator && navigator.userAgent.indexOf('Mobi') >= 0
    if (videoUnplayed && isMobile) {
      setVideoPlayerState((prevState) => ({
        ...prevState,
        isMobile,
        videoQuality: isMobile ? defaultMobileQuality : defaultDesktopQuality
      }))
    }

    // Load media
    setVideoPlayerState((prevState) => ({
      ...prevState,
      loadedSources: generateSources(prevState.videoQuality),
      loadedTracks: tracks && generateTracks()
    }))
  }

  const percentageBucket = (percentValue) => {
    if (percentValue < 25) {
      return setVideoPlayerState((prevState) => {
        if (prevState !== 'watched 0%') {
          return { ...prevState, percentageWatched: 'watched 0%' }
        }
        return false
      })
    }
    if (percentValue < 50) {
      return setVideoPlayerState((prevState) => {
        if (prevState !== 'watched 25%') {
          return { ...prevState, percentageWatched: 'watched 25%' }
        }
        return false
      })
    }
    if (percentValue < 75) {
      return setVideoPlayerState((prevState) => {
        if (prevState !== 'watched 50%') {
          return { ...prevState, percentageWatched: 'watched 50%' }
        }
        return false
      })
    }
    if (percentValue < 100) {
      return setVideoPlayerState((prevState) => {
        if (prevState !== 'watched 75%') {
          return { ...prevState, percentageWatched: 'watched 75%' }
        }
        return false
      })
    }
    if (percentValue === 100) {
      return setVideoPlayerState((prevState) => {
        if (prevState !== 'watched 100%') {
          return { ...prevState, percentageWatched: 'watched 100%' }
        }
        return false
      })
    }
    return false
  }

  // required for analytics
  const calculatePercentageWatched = () => {
    const { videoCurrentTime, videoLength, percentageWatched } = videoPlayerState

    let percentValue = (videoCurrentTime / videoLength) * 100
    percentValue = Math.round(percentValue)

    const previousValue = percentageWatched
    percentageBucket(percentValue)

    if (previousValue !== percentageWatched) {
      const analyticsObject = { name: 'video tracking', details: videoTitle }
      analyticsObject.action = percentageWatched
      analyticsTracking(analyticsObject)
    }
  }

  const setPlaying = async (isPlaying) => {
    const videoPlayer = refVideoPlayer.current

    if (isPlaying) {
      await videoPlayer.play()

      if (analyticsTracking !== undefined && videoTitle) {
        const intervalId = setInterval(calculatePercentageWatched, 300)
        setVideoPlayerState((prevState) => ({ ...prevState, intervalId }))
      }
    } else {
      const { intervalId } = videoPlayerState

      videoPlayer.pause()
      clearInterval(intervalId)
    }
  }

  const updateAnalyticsData = () => {
    const { videoIsPlaying, percentageWatched } = videoPlayerState

    const analyticsObject = { name: 'video tracking', details: videoTitle }
    analyticsObject.action = videoIsPlaying ? 'play' : 'pause'
    if (percentageWatched !== 'watched 100%') {
      analyticsTracking(analyticsObject)
    }
  }

  const setSeek = (seconds) => {
    refVideoPlayer.current.currentTime = seconds
  }

  const qualitySwitchSeek = () => {
    const { videoCurrentTime } = videoPlayerState

    // The following setState gets the video length on the splash screen in iOS
    setVideoPlayerState((prevState) => ({
      ...prevState,
      videoLength: refVideoPlayer.current.duration
    }))

    if (videoCurrentTime > -1) {
      setSeek(videoCurrentTime)
    }
  }

  const updateVideoTimestamp = () => {
    setVideoPlayerState((prevState) => ({
      ...prevState,
      videoCurrentTime: refVideoPlayer.current.currentTime
    }))
  }

  const initializeVideoDuration = () => {
    // This will run on every load() call, meaning it will also run when video quality is changed.
    setVideoPlayerState((prevState) => {
      return {
        ...prevState,
        videoLength: refVideoPlayer.current.duration,
        videoBufferEnd:
          refVideoPlayer.current.buffered.length === 0
            ? 0
            : refVideoPlayer.current.buffered.end(refVideoPlayer.current.buffered.length - 1),
        videoUnplayed: refVideoPlayer.current.played.length === 0 && !prevState.videoQualityChanged
      }
    })

    const { videoQualityChanged, videoIsPlaying } = videoPlayerState

    // Prevents an IE 11 bug where the video will not continue playing after a quality switch
    if (videoQualityChanged && videoIsPlaying) {
      setPlaying(true)
    }
  }

  const clearInactivityTimer = () => {
    setVideoPlayerState((prevState) => ({ ...prevState, mouseInactive: false }))
    clearTimeout(inactivityTimer)
  }

  const setAsBuffering = () => {
    // Prevent IE infinite buffer bug with readyState
    if (refVideoPlayer.current.readyState < 4) {
      clearInactivityTimer()
      setVideoPlayerState((prevState) => ({
        ...prevState,
        videoIsPlaying: false,
        videoIsBuffering: true,
        mouseInactive: true
      }))
      setPlaying(false)
    }
  }

  const playAfterBuffer = () => {
    const { videoIsBuffering, videoCurrentTime, videoQualityChanged } = videoPlayerState

    if (videoIsBuffering && videoCurrentTime !== -1 && !videoQualityChanged) {
      setPlaying(true)
      setVideoPlayerState((prevState) => ({ ...prevState, videoIsBuffering: false }))
    }
  }

  const resetInactivityTimer = () => {
    clearInactivityTimer()

    const { videoQualityChanged, videoIsPlaying, mouseOnControlBar } = videoPlayerState

    if (!videoQualityChanged && videoIsPlaying) {
      inactivityTimer = setTimeout(() => {
        if (!mouseOnControlBar) {
          setVideoPlayerState((prevState) => ({ ...prevState, mouseInactive: true }))
        }
      }, playerOptions.mouseTimeout)
    }
  }

  const setAsPlaying = () => {
    setVideoPlayerState((prevState) => ({
      ...prevState,
      videoIsPlaying: true,
      videoIsBuffering: false,
      videoEnded: false,
      videoUnplayed: false,
      videoQualityChanged: false
    }))

    if (analyticsTracking) {
      updateAnalyticsData()
    }

    resetInactivityTimer()
  }

  const setAsPaused = () => {
    clearInactivityTimer()
    setVideoPlayerState((prevState) => ({ ...prevState, videoIsPlaying: false }))

    if (analyticsTracking) {
      updateAnalyticsData()
    }
  }

  const returnFromEndState = () => {
    resetInactivityTimer()
    setVideoPlayerState((prevState) => ({ ...prevState, videoEnded: false }))
  }

  const setAsEnded = () => {
    setVideoPlayerState((prevState) => ({ ...prevState, videoIsPlaying: false, videoEnded: true }))
    clearInactivityTimer()
  }

  const updateBufferProgress = () => {
    const { videoCurrentTime, videoQualityChanged } = videoPlayerState
    const videoPlayer = refVideoPlayer.current
    if (videoPlayer && videoPlayer.readyState >= 2) {
      const { buffered } = videoPlayer
      setVideoPlayerState((prevState) => ({
        ...prevState,
        videoBufferEnd: buffered.length === 0 ? 0 : buffered.end(buffered.length - 1)
      }))
    } else if (videoCurrentTime !== -1 && !videoQualityChanged && !videoPlayer) {
      setVideoPlayerState((prevState) => ({
        ...prevState,
        videoIsPlaying: false,
        videoIsBuffering: true
      }))
    }
  }

  const updateVolumeState = () => {
    resetInactivityTimer()

    const videoPlayer = refVideoPlayer.current

    setVideoPlayerState((prevState) => ({
      ...prevState,
      videoCurrentVolume: videoPlayer.volume,
      videoIsMuted: videoPlayer.muted
    }))
  }

  const getPlayerWidth = () => {
    setVideoPlayerState((prevState) => ({
      ...prevState,
      videoPlayerWidth: refVideoPlayerContainer.current.offsetWidth
    }))
  }

  const setMouseOnControlBar = (isOver) => {
    setVideoPlayerState((prevState) => ({ ...prevState, mouseOnControlBar: isOver }))
  }

  const togglePlayPause = () => {
    const { videoIsPlaying } = videoPlayerState

    setPlaying(!videoIsPlaying)
  }

  const setTextTracks = (trackNumber) => {
    const { textTracks } = refVideoPlayer.current

    for (let i = 0; i < textTracks.length; i += 1) {
      textTracks[i].mode = i === trackNumber ? 'showing' : 'hidden'
    }
    setVideoPlayerState((prevState) => ({ ...prevState, selectedTextTrack: trackNumber }))
  }

  const beginVideo = () => {
    setTextTracks(-1)
    setPlaying(true)
    refVideoPlayerContainer.current.focus()
  }

  const closeSubMenus = () => {
    setVideoPlayerState((prevState) => ({
      ...prevState,
      qualityMenuOpen: false,
      captionsMenuOpen: false
    }))
  }

  const incrementSeek = (seconds) => {
    refVideoPlayer.current.currentTime += seconds
  }

  const replayVideo = async () => {
    setSeek(0)
    togglePlayPause()
  }

  const incrementVolume = (amount) => {
    refVideoPlayer.current.volume += amount
  }

  const setVolume = (amount) => {
    refVideoPlayer.current.volume = amount
  }

  const toggleMute = () => {
    refVideoPlayer.current.muted = !refVideoPlayer.current.muted
  }

  const setCaptionsMenuOpen = (isOpen) => {
    setVideoPlayerState((prevState) => ({ ...prevState, captionsMenuOpen: isOpen }))
  }

  const toggleFullscreen = () => {
    if (fscreen.fullscreenEnabled) {
      if (fscreen.fullscreenElement === null) {
        fscreen.requestFullscreen(refVideoPlayerContainer.current)
      } else {
        fscreen.exitFullscreen()
      }
      setVideoPlayerState((prevState) => {
        return { ...prevState, videoIsFullscreen: !prevState.videoIsFullscreen }
      })
    }
  }

  const setVideoQuality = async (newVideoQuality) => {
    await setPlaying(false)
    setVideoPlayerState((prevState) => ({
      ...prevState,
      videoLength: 0,
      videoBufferEnd: 0,
      videoQuality: newVideoQuality,
      loadedSources: generateSources(newVideoQuality),
      loadedTracks: tracks && generateTracks(),
      videoQualityChanged: true
    }))
    await refVideoPlayer.current.load()
    resetInactivityTimer()
    setSeek(videoPlayerState.videoCurrentTime)
  }

  const setQualityMenuOpen = (isOpen) => {
    setVideoPlayerState((prevState) => ({ ...prevState, qualityMenuOpen: isOpen }))
  }

  const handleClick = (event) => {
    resetInactivityTimer()

    // This allows playing videos within components that act like
    // links, e.g. `PreviewCard`, `StoryCard`, etc.
    event?.preventDefault()
    event?.stopPropagation()
  }

  const handleKeyboard = (event) => {
    const { videoLength, videoCurrentVolume } = videoPlayerState

    const key = event.key || event.keyCode

    // *** Begin Seek & Play Control ****
    if (key === ' ' || key === 32 || key === 'k' || key === 75) {
      event.preventDefault()
      resetInactivityTimer()
      togglePlayPause()
    }

    // Disables all keys except for play/pause when in simpleMode
    if (!simpleMode) {
      if (key === 'ArrowRight' || key === 39 || key === '.' || key === 190) {
        resetInactivityTimer()

        incrementSeek(playerOptions.keyboardSeekIncrement)
      }

      if (key === 'ArrowLeft' || key === 37 || key === ',' || key === 188) {
        resetInactivityTimer()

        incrementSeek(-playerOptions.keyboardSeekIncrement)
      }

      if (key === '0' || key === 48 || key === 'numpad 0' || key === 96) {
        setSeek(0)
      }

      if (key === '1' || key === 49 || key === 'numpad 1' || key === 97) {
        setSeek(videoLength * 0.1)
      }

      if (key === '2' || key === 50 || key === 'numpad 2' || key === 98) {
        setSeek(videoLength * 0.2)
      }

      if (key === '3' || key === 51 || key === 'numpad 3' || key === 99) {
        setSeek(videoLength * 0.3)
      }

      if (key === '4' || key === 52 || key === 'numpad 4' || key === 100) {
        setSeek(videoLength * 0.4)
      }

      if (key === '5' || key === 53 || key === 'numpad 5' || key === 101) {
        setSeek(videoLength * 0.5)
      }

      if (key === '6' || key === 54 || key === 'numpad 6' || key === 102) {
        setSeek(videoLength * 0.6)
      }

      if (key === '7' || key === 55 || key === 'numpad 7' || key === 103) {
        setSeek(videoLength * 0.7)
      }

      if (key === '8' || key === 56 || key === 'numpad 8' || key === 104) {
        setSeek(videoLength * 0.8)
      }

      if (key === '9' || key === 57 || key === 'numpad 9' || key === 105) {
        setSeek(videoLength * 0.9)
      }

      // **** End Seek & Play Control ****

      // **** Begin Volume Control ****

      const { keyboardVolumeIncrement } = playerOptions

      if (
        key === 'ArrowUp' ||
        key === 38 ||
        key === '=' ||
        key === 187 ||
        key === 'add' ||
        key === 107
      ) {
        resetInactivityTimer()

        if (videoCurrentVolume + keyboardVolumeIncrement < 1) {
          incrementVolume(keyboardVolumeIncrement)
        } else {
          setVolume(1)
        }
      }

      if (
        key === 'ArrowDown' ||
        key === 40 ||
        key === '-' ||
        key === 189 ||
        key === 'subtract' ||
        key === 109
      ) {
        resetInactivityTimer()

        if (videoCurrentVolume - keyboardVolumeIncrement > 0) {
          incrementVolume(-keyboardVolumeIncrement)
        } else {
          setVolume(0)
        }
      }

      if (key === 'm' || key === 77) {
        resetInactivityTimer()

        toggleMute()
      }

      if (key === 'f' || key === 70) {
        resetInactivityTimer()

        toggleFullscreen()
      }

      if (key === 'Escape' || key === 27) {
        // Resets focus back to container if user is focused on ControlBar button
        refVideoPlayerContainer.current.focus()
        closeSubMenus()
      }

      if (key === 'q' || key === 81) {
        refKeyboardInstructions.current.focus()
      }
    }
  }

  // Prepares video and caption files
  React.useEffect(() => {
    refreshMedia()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // Initial Setup after loading sources
  React.useEffect(() => {
    const { loadedSources, loadedTracks } = videoPlayerState

    if (loadedSources && loadedTracks) {
      const videoPlayer = refVideoPlayer.current

      // Initializes Settings
      videoPlayer.volume = defaultVolume / 100
      videoPlayer.muted = beginMuted || simpleMode

      getPlayerWidth()

      // Reports when the video has completed loading metadata (used for seeking after quality switch)
      videoPlayer.onloadedmetadata = qualitySwitchSeek

      // Reports the video's duration when the video player is ready to play
      videoPlayer.oncanplay = initializeVideoDuration

      // Reports the video's width on resize
      window.addEventListener('resize', getPlayerWidth)

      // Reports the current video timestamp
      videoPlayer.ontimeupdate = updateVideoTimestamp

      // Reports when the video has paused due to buffering
      videoPlayer.onwaiting = setAsBuffering

      // Reports the video's latest buffering progress if video player is properly initialized
      videoPlayer.onprogress = updateBufferProgress

      // Reports when the video has recovered from buffering
      videoPlayer.oncanplaythrough = playAfterBuffer

      // Reports when the video has ended
      videoPlayer.onended = setAsEnded

      // Reports when the video is playing and disables the buffer spinner (even if buffering is still needed)
      videoPlayer.onplay = setAsPlaying

      // Reports when the video has been paused
      videoPlayer.onpause = setAsPaused

      // Reports when the video has been seeked
      videoPlayer.onseeked = returnFromEndState

      // Reports when the video's volume has been changed, or if the video has been muted
      videoPlayer.onvolumechange = updateVolumeState

      // Video Analytics
      if (
        (analyticsTracking !== undefined && videoTitle === undefined) ||
        (analyticsTracking === undefined && videoTitle !== undefined)
      ) {
        warn(
          'Video',
          'Both the `analyticsTracking` and `videoTitle` props must be defined in order to return a proper analytics object.'
        )
      }

      return () => {
        clearInactivityTimer()
        window.removeEventListener('resize', getPlayerWidth)
        clearInterval(videoPlayerState.intervalId)
      }
    }

    return () => {} // returning a noop for sake of consistency
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [videoPlayerState.loadedSources, videoPlayerState.loadedTracks])

  const {
    loadedSources,
    loadedTracks,
    isMobile,
    mouseInactive,
    selectedTextTrack,
    qualityMenuOpen,
    captionsMenuOpen,
    videoUnplayed,
    videoEnded,
    videoIsBuffering,
    videoIsPlaying,
    videoIsFullscreen,
    videoIsMuted,
    videoQualityChanged,
    videoLength,
    videoBufferEnd,
    videoCurrentTime,
    videoCurrentVolume,
    videoQuality,
    videoPlayerWidth
  } = videoPlayerState
  const { style, className, as, ...safeRest } = rest

  return (
    <VideoPlayerContainer
      {...safeRest}
      ref={refVideoPlayerContainer}
      videoBorder={videoBorder}
      borderColor={borderColor}
      onMouseMove={resetInactivityTimer}
      onClick={handleClick}
      onKeyDown={handleKeyboard}
      tabIndex="0"
      aria-label={simpleMode ? videoText[copy].videoSelectedSimple : videoText[copy].videoSelected}
      data-testid="videoPlayer"
      {...selectProps(rest)}
    >
      <VideoOverlayContainer
        mouseInactive={mouseInactive}
        onClick={() => {
          closeSubMenus()
          togglePlayPause()
        }}
      >
        {/* ======== Video Splash Screen ======== */}
        {videoUnplayed && !videoQualityChanged && (
          <VideoSplashContainer>
            <VideoSplash
              poster={posterSrc}
              copy={copy}
              videoLength={videoLength}
              simpleMode={simpleMode}
              iconLeftOffsetPx={2}
              onClick={beginVideo}
            />
          </VideoSplashContainer>
        )}
        {/* =================================== */}
        <VideoOverlayElementContainer>
          {/* ======== Replay Button ======== */}
          {videoEnded && <MiddleControlButton icon={replayIcon} onClick={replayVideo} />}
          {/* ================================ */}
          {/* ======== Middle Play/Pause Button ======= */}
          {!videoUnplayed && !videoIsBuffering && !videoEnded && !isMobile && (
            <MiddleControlButton
              icon={videoIsPlaying ? pauseIcon : playIcon}
              isHidden={mouseInactive}
              onClick={togglePlayPause}
              onFocus={resetInactivityTimer}
            />
          )}
          {/* ========================================== */}

          {/* ======== Spinner Display ======= */}
          {videoIsBuffering && !isMobile && <Spinner show />}
          {/* ================================ */}
        </VideoOverlayElementContainer>
      </VideoOverlayContainer>

      {/* ======== Video Element ======= */}
      <VideoElementContainer videoIsFullscreen={videoIsFullscreen} mouseInactive={mouseInactive}>
        <VideoPlayer
          ref={refVideoPlayer}
          controls={isMobile}
          videoIsFullscreen={videoIsFullscreen}
          crossOrigin={crossOrigin}
          playsinline
        >
          {loadedSources}
          {loadedTracks}
          Your browser does not support the video tag.
        </VideoPlayer>
      </VideoElementContainer>
      {/* ============================== */}

      {!simpleMode && (
        <ControlBar
          videoPlayer={refVideoPlayer}
          videoPlayerContainer={refVideoPlayerContainer}
          sources={sources}
          tracks={tracks}
          videoLength={videoLength}
          videoBufferEnd={videoBufferEnd}
          videoCurrentTime={videoCurrentTime}
          videoPlaying={refVideoPlayer.current !== null ? !refVideoPlayer.current.paused : false}
          videoUnplayed={videoUnplayed}
          videoCurrentVolume={videoCurrentVolume}
          videoIsMuted={videoIsMuted}
          setVolume={setVolume}
          isHidden={(mouseInactive || videoUnplayed) && !videoEnded}
          isMobile={isMobile}
          tracksAvailable={tracks !== undefined}
          togglePlayPause={togglePlayPause}
          setSeek={setSeek}
          toggleMute={toggleMute}
          toggleFullscreen={toggleFullscreen}
          videoIsFullscreen={videoIsFullscreen}
          setTextTracks={setTextTracks}
          selectedTextTrack={selectedTextTrack}
          resetInactivityTimer={resetInactivityTimer}
          videoQuality={videoQuality}
          setVideoQuality={setVideoQuality}
          qualityMenuOpen={qualityMenuOpen}
          setQualityMenuOpen={setQualityMenuOpen}
          captionsMenuOpen={captionsMenuOpen}
          setCaptionsMenuOpen={setCaptionsMenuOpen}
          clearInactivityTimer={clearInactivityTimer}
          copy={copy}
          compactModeThreshold={playerOptions.compactModeThreshold}
          videoPlayerWidth={videoPlayerWidth}
          onMouseOver={() => setMouseOnControlBar(true)}
          onMouseOut={() => setMouseOnControlBar(false)}
          onFocus={() => {}}
          onBlur={() => {}}
        />
      )}

      {/* ======== Screen Reader Keyboard Instructions ======= */}
      <span
        tabIndex="-1"
        ref={refKeyboardInstructions}
        aria-label={videoText[copy].videoPlayer}
        role="note"
      />
      {/* ==================================================== */}
    </VideoPlayerContainer>
  )
}

Video.propTypes = {
  ...selectedSystemPropTypes,
  /**
   * An array of objects that defines each video file. See the "Basic Usage" section for more information.
   */
  sources: PropTypes.arrayOf(
    PropTypes.shape({
      /** A path to a video file */
      source: PropTypes.string.isRequired,
      /** The video's MIME type (example: `video/mp4`) */
      mediaType: PropTypes.string.isRequired,
      /** The label to be given to this file in the quality selector (example: `1080p`) */
      qualityName: PropTypes.string.isRequired,
      /** A number indicating the video's quality, with 1 being the best, and the number increasing from there as quality degrades */
      qualityRank: PropTypes.number.isRequired,
      /** A boolean that defines its source as a fallback for another source with the same `qualityRank` */
      isFallback: PropTypes.bool
    })
  ).isRequired,
  /**
   * A path of the image that will be displayed on the video's splash screen.
   */
  posterSrc: PropTypes.string.isRequired,
  /**
   * An array of objects that defines each caption file. See the "Basic Usage" section for more information.
   */
  tracks: PropTypes.arrayOf(
    PropTypes.shape({
      /** A path to the track file */
      source: PropTypes.string.isRequired,
      /** The name of the track file as it will appear in the "Captions" dialogue */
      label: PropTypes.string.isRequired,
      /** The track's file type, typically one of "subtitles", "captions", or "descriptions". See [MDN's documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track#Attributes) for more information on these types */
      kind: PropTypes.string.isRequired,
      /** The language of the track file represented as a ISO 639-1 language code */
      language: PropTypes.string.isRequired
    })
  ),
  /**
   * The video's default volume, defined from 1 to 100. Please use the `beginMuted` prop to have the video start silenced.
   */
  defaultVolume: PropTypes.number,
  /**
   * Defines if the video should start muted.
   */
  beginMuted: PropTypes.bool,
  /**
   * The default quality level if the user is on mobile. See the `sources` prop for available quality levels.
   */
  defaultMobileQuality: PropTypes.number,
  /**
   * The default quality level if the user is on desktop. See the `sources` prop for available quality levels.
   */
  defaultDesktopQuality: PropTypes.number,
  /**
   * The video player UI's language as an ISO language code. It currently supports English and French.
   */
  copy: PropTypes.oneOf(['en', 'fr']).isRequired,
  /**
   * Sets the `video` tag's `crossorigin` mode. Please note that content loaded without CORS approval may be insecure.
   * @since 1.1.0
   */
  crossOrigin: PropTypes.oneOf(['anonymous', 'use-credentials']),
  /**
   * Disables the control bar during playback and reduces the amount of time until the UI hides itself.
   * For special approved internal uses only.
   * @since 1.2.0
   */
  simpleMode: PropTypes.bool,
  /**
   * Renders a gainsboro coloured border around the video. Used to seperate the video from the rest of the page if the video's background is the same colour as the container's.
   * @since 1.2.0
   */
  videoBorder: PropTypes.bool,
  /**
   * A callback function that fires when a customer interacts with Video.
   * When using `analyticsTracking`, `videoTitle` must also be defined.
   *
   * When invoked, your callback function returns an object containing three keys (see below).
   * @since 1.3.0
   *
   * @param {Object} analyticsObject Contains video data based on customer interactions
   * @param {string} analyticsObject.name Always 'video tracking'
   * @param {string} analyticsObject.action Contains the latest customer action
   * @param {string} analyticsObject.details The name of the video based on the `videoTitle` prop
   */
  analyticsTracking: PropTypes.func,
  /**
   * The title of the video being rendered. This is used for analytics purposes in conjunction with `analyticsTracking`.
   * @since 1.3.0
   */
  videoTitle: PropTypes.string,
  tokens: getTokensPropType('Video'),
  variant: variantProp.propType
}

export default Video
