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

import size from 'lodash/size'

import { useFullscreen } from 'features/StreamPlayer/hooks/useFullscreen'
import { useVolume } from 'features/VideoPlayer/hooks/useVolume'
import { useMatchPopupStore } from 'features/MatchPopup'

import { useObjectState } from 'hooks'

import { useProgressChangeHandler } from './useProgressChangeHandler'
import { usePlayingHandlers } from './usePlayingHandlers'
import { useVideoQuality } from './useVideoQuality'
import { useDuration } from './useDuration'
import type { Chapters } from '../types'
import { Players } from '../types'
import { getNextPlayer } from '../helpers'
import { REWIND_SECONDS } from '../config'

const initialState = {
  activeChapterIndex: 0,
  activePlayer: Players.PLAYER1,
  loadedProgress: 0,
  playedProgress: 0,
  playing: false,
  ready: false,
  seek: {
    [Players.PLAYER1]: 0,
    [Players.PLAYER2]: 0,
  },
  seeking: false,
}

export type PlayerState = typeof initialState

export type Props = {
  chapters: Chapters,
  onError?: () => void,
  onPlayingChange: (playing: boolean) => void,
}

export const useMultiSourcePlayer = ({
  chapters,
  onError,
  onPlayingChange,
}: Props) => {
  const numberOfChapters = size(chapters)
  const [
    {
      activeChapterIndex: chapterIndex,
      activePlayer,
      loadedProgress,
      playedProgress,
      playing,
      ready,
      seek,
      seeking,
    },
    setPlayerState,
  ] = useObjectState(initialState)
  const video1Ref = useRef<HTMLVideoElement>(null)
  const video2Ref = useRef<HTMLVideoElement>(null)
  const activeChapterIndex = numberOfChapters >= chapterIndex ? chapterIndex : 0
  const {
    onReady,
    playNextChapter,
    playPrevChapter,
    startPlaying,
    togglePlaying,
  } = usePlayingHandlers(setPlayerState, numberOfChapters)

  const {
    selectedQuality,
    setSelectedQuality,
    videoQualities,
  } = useVideoQuality(chapters)

  const duration = useDuration(chapters)

  const handleError = useCallback(() => {
    onError?.()
  }, [onError])

  const getActiveChapter = useCallback(
    (index: number = activeChapterIndex) => chapters[index],
    [chapters, activeChapterIndex],
  )

  const videoRef = [video1Ref, video2Ref][activePlayer]
  const isFirstChapterPlaying = activeChapterIndex === 0
  const isLastChapterPlaying = activeChapterIndex === numberOfChapters - 1
  const setPlayerTime = (progressMs: number) => {
    if (!videoRef.current) return
    videoRef.current.currentTime = progressMs / 1000
  }
  const rewindForward = () => {
    const chapter = getActiveChapter()
    const newProgress = playedProgress + REWIND_SECONDS * 1000
    if (newProgress <= chapter.duration) {
      setPlayerTime(chapter.startOffsetMs + newProgress)
    } else if (isLastChapterPlaying) {
      playNextChapter()
    } else {
      const nextChapter = getActiveChapter(activeChapterIndex + 1)
      const fromMs = newProgress - chapter.duration
      playNextChapter(fromMs, nextChapter.startOffsetMs)
    }
  }

  const rewindBackward = () => {
    const chapter = getActiveChapter()
    const newProgress = playedProgress - REWIND_SECONDS * 1000
    if (newProgress >= 0) {
      setPlayerTime(chapter.startOffsetMs + newProgress)
    } else if (isFirstChapterPlaying) {
      setPlayerTime(chapter.startOffsetMs)
    } else {
      const prevChapter = getActiveChapter(activeChapterIndex - 1)
      const fromMs = prevChapter.duration + newProgress
      playPrevChapter(fromMs, prevChapter.startOffsetMs)
    }
  }

  const onProgressChange = useProgressChangeHandler({
    chapters,
    duration,
    setPlayerState,
  })

  const getChapterUrl = useCallback((index: number, quality: string = selectedQuality) => (
    getActiveChapter(index)?.urls[quality]
  ), [selectedQuality, getActiveChapter])

  const onQualitySelect = (quality: string) => {
    setPlayerState((state) => ({
      seek: {
        ...state.seek,
        [state.activePlayer]: videoRef.current?.currentTime ?? 0,
      },
    }))
    setSelectedQuality(quality)
  }

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

  const onLoadedProgress = (loadedMs: number) => {
    const chapter = getActiveChapter()
    const value = loadedMs - chapter.startOffsetMs

    setPlayerState({ loadedProgress: value })
  }

  const onPlayedProgress = (playedMs: number) => {
    const chapter = getActiveChapter()
    const value = Math.max(playedMs - chapter.startOffsetMs, 0)

    setPlayerState({ playedProgress: value })
  }

  const onEnded = () => {
    playNextChapter()
  }

  useEffect(() => {
    onPlayingChange(playing)
  }, [playing, onPlayingChange])

  useEffect(() => {
    setPlayerState((state) => ({
      ...initialState,
      activePlayer: getNextPlayer(state.activePlayer),
      playing: state.playing,
      ready: state.ready,
    }))
  }, [chapters, setPlayerState])

  useEffect(() => {
    const { duration: chapterDuration } = getActiveChapter()
    if (playedProgress >= chapterDuration && !seeking) {
      playNextChapter()
    }
  }, [
    getActiveChapter,
    playedProgress,
    seeking,
    playNextChapter,
  ])

  const { selectedPlaylist } = useMatchPopupStore()

  useEffect(() => {
    if (selectedPlaylist) {
      startPlaying()
    }
  }, [selectedPlaylist, startPlaying])

  return {
    activeChapterIndex,
    activePlayer,
    activeSrc: getChapterUrl(activeChapterIndex),
    allPlayedProgress: playedProgress + getActiveChapter().startMs,
    chapters,
    duration,
    isFirstChapterPlaying,
    isLastChapterPlaying,
    loadedProgress,
    nextSrc: getChapterUrl((activeChapterIndex + 1) % numberOfChapters),
    numberOfChapters,
    onEnded,
    onError: handleError,
    onLoadedProgress,
    onPlayedProgress,
    onPlayerClick,
    onProgressChange,
    onQualitySelect,
    onReady,
    playNextChapter,
    playPrevChapter,
    playedProgress,
    playing,
    ready,
    rewindBackward,
    rewindForward,
    seek,
    selectedQuality,
    togglePlaying,
    video1Ref,
    video2Ref,
    videoQualities,
    ...useFullscreen(),
    ...useVolume(),
  }
}
