import {
  useState,
  useEffect,
  useCallback,
} from 'react'

import Hls from 'hls.js'

import map from 'lodash/map'
import find from 'lodash/find'
import uniqBy from 'lodash/uniqBy'
import isEmpty from 'lodash/isEmpty'
import orderBy from 'lodash/orderBy'
import isString from 'lodash/isString'
import filter from 'lodash/fp/filter'

import { useLocalStore } from 'hooks'

const autoQuality = {
  label: 'Auto',
  level: -1,
}

/**
 * Убирает из списка качества без height
 *
 * Когда в манифесте нет данных о качествах стрима hls.levels содержит
 * непонятное качество без свойств height, width и тд для определения
 * какое это качество
 */
const filterOutUnknownQualities = filter(({ height }: Hls.Level) => Boolean(height))

const getVideoQualities = (levels: Array<Hls.Level>) => {
  if (isEmpty(levels)) return []

  const filteredQualities = filterOutUnknownQualities(levels)
  const qualities = map(filteredQualities, (level, i) => ({
    label: String(level.height),
    level: i,
  }))
  const sorted = orderBy(
    qualities,
    Number,
    'desc',
  )
  return uniqBy([...sorted, autoQuality], 'label')
}

export const useVideoQuality = (hls: Hls) => {
  const [videoQualities, setVideoQualities] = useState([autoQuality])
  const [selectedQuality, setSelectedQuality] = useLocalStore({
    defaultValue: autoQuality.label,
    key: 'player_quality',
    validator: isString,
  })

  const onQualitySelect = useCallback((label: string) => {
    const quality = find(videoQualities, { label })
    if (!quality || quality.level === hls.currentLevel) return
    // eslint-disable-next-line no-param-reassign
    hls.currentLevel = quality.level
    setSelectedQuality(quality.label)
  }, [
    setSelectedQuality,
    videoQualities,
    hls,
  ])

  useEffect(() => {
    const listener = () => {
      const qualities = getVideoQualities(hls.levels)
      const quality = find(qualities, { label: selectedQuality }) || autoQuality

      // eslint-disable-next-line no-param-reassign
      hls.currentLevel = quality.level
      setSelectedQuality(quality.label)
      setVideoQualities(qualities)
    }
    hls.on(Hls.Events.MANIFEST_PARSED, listener)

    return () => {
      hls.off(Hls.Events.MANIFEST_PARSED, listener)
    }
  }, [
    selectedQuality,
    setSelectedQuality,
    hls,
  ])

  return {
    onQualitySelect,
    selectedQuality,
    videoQualities: map(videoQualities, 'label'),
  }
}
