import type { MouseEvent } from 'react'
import {
  useCallback,
  useEffect,
  useMemo,
} from 'react'

import once from 'lodash/once'

import { useVolume } from 'features/VideoPlayer/hooks/useVolume'
import { REWIND_SECONDS } from 'features/MultiSourcePlayer/config'
import { useNoNetworkPopupStore } from 'features/NoNetworkPopup'

import { useObjectState } from 'hooks'

import type { MatchInfo } from 'requests/getMatchInfo'

import { isIOS } from 'config/userAgent'

import { useHlsPlayer } from './useHlsPlayer'
import { useFullscreen } from './useFullscreen'
import { useVideoQuality } from './useVideoQuality'
import { useControlsVisibility } from './useControlsVisibility'

const toMilliSeconds = (seconds: number) => seconds * 1000

const initialState = {
  duration: 0,
  loadedProgress: 0,
  playedProgress: 0,
  playing: false,
  ready: false,
  seek: 0,
}

export type Props = {
  onPlayingChange: (playing: boolean) => void,
  onProgressChange: (seconds: number) => void,
  profile: MatchInfo,
  resumeFrom?: number,
  url: string,
}

export const useVideoPlayer = ({
  onPlayingChange,
  onProgressChange: progressChangeCallback,
  resumeFrom,
  url,
}: Props) => {
  const { hls, videoRef } = useHlsPlayer(url, resumeFrom)

  const [{
    duration,
    loadedProgress,
    playedProgress,
    playing,
    ready,
    seek,
  }, setPlayerState] = useObjectState({
    ...initialState,
    playedProgress: toMilliSeconds(resumeFrom || 0),
    seek: resumeFrom || 0,
  })

  const startPlaying = useMemo(() => once(() => {
    setPlayerState({ playing: true, ready: true })
    onPlayingChange(true)
  }), [onPlayingChange, setPlayerState])

  const {
    isFullscreen,
    onFullscreenClick,
    wrapperRef,
  } = useFullscreen()

  const togglePlaying = () => {
    if (ready) {
      setPlayerState({ playing: !playing })
      onPlayingChange(!playing)
    }
  }

  const rewind = (seconds: number) => () => {
    if (!videoRef.current) return
    const { currentTime } = videoRef.current
    const newProgress = currentTime + seconds
    videoRef.current.currentTime = newProgress
  }

  const onError = useCallback(() => {
    setPlayerState({ playing: false })
  }, [setPlayerState])

  const onPlayerClick = (e: MouseEvent<HTMLDivElement>) => {
    if (e.target === videoRef.current) {
      togglePlaying()
    }
  }

  const onDuration = (durationSeconds: number) => {
    setPlayerState({ duration: toMilliSeconds(durationSeconds) })
  }

  const onProgressChange = useCallback((progress: number) => {
    const progressMs = progress * duration
    setPlayerState({ playedProgress: progressMs, seek: progressMs / 1000 })
  }, [duration, setPlayerState])

  const onLoadedProgress = (loadedMs: number) => {
    setPlayerState({ loadedProgress: loadedMs })
  }

  const onPlayedProgress = (playedMs: number) => {
    setPlayerState({ playedProgress: playedMs })
    progressChangeCallback(playedMs / 1000)
  }

  const backToLive = useCallback(() => {
    const liveProgressMs = Math.max(duration - 10000, 0)
    setPlayerState({ playedProgress: liveProgressMs, seek: liveProgressMs / 1000 })
  }, [duration, setPlayerState])

  const { isOnline } = useNoNetworkPopupStore()

  useEffect(() => {
    if (!isOnline) {
      setPlayerState({ playing: false })
      onPlayingChange(false)
    }
  }, [
    isOnline,
    onPlayingChange,
    setPlayerState,
  ])

  useEffect(() => {
    if (!navigator.serviceWorker || !isIOS) return undefined
    const listener = (event: MessageEvent) => {
      setPlayerState({ duration: toMilliSeconds(event.data.duration) })
    }

    navigator.serviceWorker.addEventListener('message', listener)
    return () => {
      navigator.serviceWorker.removeEventListener('message', listener)
    }
  }, [setPlayerState])

  return {
    backToLive,
    duration,
    isFullscreen,
    loadedProgress,
    onDuration,
    onError,
    onFullscreenClick,
    onLoadedProgress,
    onPlayedProgress,
    onPlayerClick,
    onProgressChange,
    playedProgress,
    playing,
    ready,
    rewindBackward: rewind(-REWIND_SECONDS),
    rewindForward: rewind(REWIND_SECONDS),
    seek,
    startPlaying,
    togglePlaying,
    videoRef,
    wrapperRef,
    ...useControlsVisibility(isFullscreen),
    ...useVolume(),
    ...useVideoQuality(hls),
  }
}
