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

import size from 'lodash/size'

import type { LastPlayPosition } from 'requests'

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

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'

export type PlayerState = {
  activeChapterIndex: number,
  activePlayer: Players,
  loadedProgress: number,
  playedProgress: number,
  playing: boolean,
  ready: boolean,
  seek: Record<Players, number>,
  seeking: boolean,
}

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

export type Props = {
  chapters: Chapters,
  onError?: () => void,
  onPlayingChange: (playing: boolean) => void,
  onProgressChange: (seconds: number, period: number) => void,
  resumeFrom: LastPlayPosition,
}

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

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

  const duration = useDuration(chapters)

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

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

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

  const videoRef = [video1Ref, video2Ref][activePlayer]

  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 = chapters[activeChapterIndex]
    const value = loadedMs - chapter.startOffsetMs

    setPlayerState({ loadedProgress: value })
  }

  const onPlayedProgress = (playedMs: number) => {
    const chapter = chapters[activeChapterIndex]
    const value = Math.max(playedMs - chapter.startOffsetMs, 0)

    setPlayerState({ playedProgress: value })
  }

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

  useEffect(() => {
    const progressSeconds = playedProgress / 1000
    const { period } = chapters[activeChapterIndex]
    onProgressChangeCallback(progressSeconds, Number(period))
  }, [
    playedProgress,
    chapters,
    onProgressChangeCallback,
    activeChapterIndex,
  ])

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

  return {
    activeChapterIndex,
    activePlayer,
    activeSrc: getChapterUrl(activeChapterIndex),
    chapters,
    duration,
    isFirstChapterPlaying: activeChapterIndex === 0,
    isLastChapterPlaying: activeChapterIndex === numberOfChapters - 1,
    loadedProgress,
    nextSrc: getChapterUrl(activeChapterIndex + 1),
    onError: handleError,
    onLoadedProgress,
    onPlayedProgress,
    onPlayerClick,
    onProgressChange,
    onQualitySelect,
    onReady,
    playNextChapter,
    playPrevChapter,
    playedProgress,
    playing,
    ready,
    seek,
    selectedQuality,
    startPlaying,
    togglePlaying,
    video1Ref,
    video2Ref,
    videoQualities,
    ...useFullscreen(),
    ...useVolume(),
  }
}
