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'

export type PlayerState = {
  activeChapterIndex: number,
  loadedProgress: number,
  playedProgress: number,
  playing: boolean,
  ready: boolean,
  seek: number,
  seeking: boolean,
}

const initialState: PlayerState = {
  activeChapterIndex: 0,
  loadedProgress: 0,
  playedProgress: 0,
  playing: false,
  ready: false,
  seek: 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 [
    {
      activeChapterIndex,
      loadedProgress,
      playedProgress,
      playing,
      seek,
      seeking,
    },
    setPlayerState,
  ] = useObjectState({ ...initialState, activeChapterIndex: resumeFrom.half })
  const videoRef = useRef<HTMLVideoElement>(null)
  const {
    onReady,
    startPlaying,
    stopPlaying,
    togglePlaying,
  } = usePlayingHandlers(setPlayerState)

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

  const duration = useDuration(chapters)

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

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

  const getActiveChapterUrl = useCallback((quality: string = selectedQuality) => (
    chapters[activeChapterIndex].urls[quality]
  ), [selectedQuality, chapters, activeChapterIndex])

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

  const playNextChapter = useCallback(() => {
    const nextIndex = (activeChapterIndex + 1) % size(chapters)
    setPlayerState({
      activeChapterIndex: nextIndex,
      loadedProgress: 0,
      playedProgress: 0,
      playing: nextIndex !== 0,
    })
  }, [activeChapterIndex, chapters, setPlayerState])

  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,
    activeSrc: getActiveChapterUrl(),
    chapters,
    duration,
    loadedProgress,
    onError: handleError,
    onLoadedProgress,
    onPlayedProgress,
    onPlayerClick,
    onProgressChange,
    onQualitySelect,
    onReady,
    playNextChapter,
    playedProgress,
    playing,
    seek,
    selectedQuality,
    startPlaying,
    togglePlaying,
    videoQualities,
    videoRef,
    ...useFullscreen(),
    ...useVolume(),
  }
}
