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

import size from 'lodash/size'

import { useControlsVisibility } from 'features/StreamPlayer/hooks/useControlsVisibility'
import { useFullscreen } from 'features/StreamPlayer/hooks/useFullscreen'
import { useVolume } from 'features/VideoPlayer/hooks/useVolume'
import { useNoNetworkPopupStore } from 'features/NoNetworkPopup'
import { useMatchPageStore } from 'features/MatchPage/store'
import { FULL_GAME_KEY } from 'features/MatchPage/helpers/buildPlaylists'

import {
  useEventListener,
  useInterval,
  useObjectState,
  usePageParams,
} from 'hooks'
import type { SetPartialState } from 'hooks'

import {
  MatchInfo,
  saveMatchStats,
  VIEW_INTERVAL_MS,
} from 'requests'

import type { PausedData } from 'features/MatchPage/components/FinishedMatch/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 = {
  access: boolean,
  chapters: Chapters,
  isOpenPopup?: boolean,
  onError?: () => void,
  onPlayingChange: (playing: boolean) => void,
  pausedData: PausedData,
  profile: MatchInfo,
  setPausedData: SetPartialState<PausedData>,
}

export const useMultiSourcePlayer = ({
  chapters,
  onError,
  onPlayingChange,
  pausedData,
  setPausedData,
}: Props) => {
  const {
    handlePlaylistClick,
    isPlayFilterEpisodes,
    matchPlaylists,
    playNextEpisode,
    selectedPlaylist,
    setCircleAnimation,
    setPlayingProgress,
  } = useMatchPageStore()

  const { profileId, sportType } = usePageParams()

  /** время для сохранения статистики просмотра матча */
  const timeForStatistics = useRef(0)

  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,
    stopPlaying,
    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 {
    isFullscreen,
    onFullscreenClick,
    wrapperRef,
  } = useFullscreen(videoRef)

  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)

    timeForStatistics.current = (value + chapter.startMs) / 1000

    setPlayerState({ playedProgress: value })
    setPlayingProgress(Math.floor(value / 1000))

    if (chapter.isFullMatchChapter) {
      setPausedData({
        activeChapterIndex,
        activePlayer,
        playedProgress: value,
      })
    }
  }

  const backToPausedTime = useCallback(() => {
    if (selectedPlaylist?.id !== FULL_GAME_KEY) {
      handlePlaylistClick(matchPlaylists.match[0])
    }

    setTimeout(() => {
      setPlayerState((state) => ({
        activeChapterIndex: pausedData.activeChapterIndex,
        playedProgress: pausedData.playedProgress,
        seek: {
          ...state.seek,
          [pausedData.activePlayer]: pausedData.playedProgress / 1000,
        },
      }))
    }, 0)
  }, [
    selectedPlaylist?.id,
    pausedData,
    handlePlaylistClick,
    matchPlaylists.match,
    setPlayerState,
  ])

  const onEnded = () => {
    playNextChapter()
  }
  const onPause = () => {
    setPlayerState({ playing: false })
  }

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

  useEffect(() => {
    setPlayerState({ ...initialState })
  }, [profileId, setPlayerState])

  useEffect(() => {
    setCircleAnimation((state) => ({
      ...state,
      playedProgress,
      playing,
      ready,
    }))
  }, [
    playedProgress,
    playing,
    ready,
    setCircleAnimation,
  ])

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

  useEffect(() => {
    const { duration: chapterDuration } = getActiveChapter()
    if (playedProgress >= chapterDuration && !seeking && isPlayFilterEpisodes) {
      setPlayerState({ playedProgress: 0 })
      playNextEpisode()
    }
    if (playedProgress >= chapterDuration && !seeking && !isPlayFilterEpisodes) {
      if (isLastChapterPlaying) {
        backToPausedTime()
      } else {
        playNextChapter()
      }
    }
  }, [
    isPlayFilterEpisodes,
    playNextEpisode,
    getActiveChapter,
    playedProgress,
    seeking,
    playNextChapter,
    setPlayerState,
    isLastChapterPlaying,
    backToPausedTime,
  ])

  useEventListener({
    callback: (e: KeyboardEvent) => {
      if (e.code === 'ArrowLeft') rewindBackward()
      else if (e.code === 'ArrowRight') rewindForward()
    },
    event: 'keydown',
  })

  const { isOnline } = useNoNetworkPopupStore()

  useEffect(() => {
    if (!isOnline) {
      stopPlaying()
    }
  }, [
    isOnline,
    stopPlaying,
  ])

  useEffect(() => {
    if (ready && videoRef) {
      videoRef.current?.scrollIntoView({
        behavior: 'smooth',
        block: 'start',
      })
    }
  }, [ready, videoRef])

  // ведем статистику просмотра матча
  const { start: startCollectingStats, stop: stopCollectingStats } = useInterval({
    callback: () => {
      if (timeForStatistics.current !== 0) {
        saveMatchStats({
          matchId: profileId,
          matchSecond: timeForStatistics.current,
          sportType,
        })
      }
    },
    intervalDuration: VIEW_INTERVAL_MS,
    startImmediate: false,
  })

  useEffect(() => {
    if (playing) {
      startCollectingStats()
    } else {
      stopCollectingStats()
    }
  }, [
    playing,
    startCollectingStats,
    stopCollectingStats,
    profileId,
  ])

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