import map from 'lodash/map'
import last from 'lodash/last'
import uniq from 'lodash/uniq'
import filter from 'lodash/filter'
import reduce from 'lodash/reduce'
import concat from 'lodash/concat'
import orderBy from 'lodash/orderBy'
import isEmpty from 'lodash/isEmpty'
import groupBy from 'lodash/groupBy'

import type {
  Videos,
  Episodes,
  Episode,
} from 'requests'

import type { Chapters, Urls } from 'features/MultiSourcePlayer/types'

import type { PlaylistOption } from '../../types'
import { FULL_GAME_KEY } from '../../helpers/buildPlaylists'

const getUniquePeriods = (videos: Videos) => uniq(map(videos, ({ period }) => period))

type Video = {
  duration: number,
  period: number,
  urls: Urls,
}

const getVideoByPeriod = (videos: Videos, period: number) => {
  const videosWithSamePeriod = filter(videos, { period })
  if (isEmpty(videosWithSamePeriod)) return null

  const urls = reduce(
    videosWithSamePeriod,
    (acc: Urls, video) => ({
      ...acc,
      [video.quality]: video.url,
    }),
    {},
  )
  const [video] = videosWithSamePeriod
  return {
    duration: video.duration,
    period: video.period,
    urls,
  }
}

const getVideoByPeriods = (videos: Videos, periods: Array<number>) => (
  reduce(
    periods,
    (acc: Array<Video>, period) => {
      const video = getVideoByPeriod(videos, period)
      return video ? concat(acc, video) : acc
    },
    [],
  )
)

const getFullMatchChapters = (videos: Array<Video>) => {
  const sortedVideos = orderBy(videos, ({ period }) => period)
  return reduce(
    sortedVideos,
    (acc: Chapters, video) => {
      const prevVideoEndMs = last(acc)?.endMs || 0
      const endMs = prevVideoEndMs + video.duration
      const nextChapter = {
        duration: video.duration,
        endMs,
        endOffsetMs: endMs,
        isFullMatchChapter: true,
        period: video.period,
        startMs: prevVideoEndMs,
        startOffsetMs: 0,
        urls: video.urls,
      }
      return concat(acc, nextChapter)
    },
    [],
  )
}

const getEpisodeUrls = (urls: Urls, episode: Episode) => reduce(
  urls,
  (
    acc: Urls,
    url,
    qulaity,
  ) => {
    acc[qulaity] = `${url}#t=${episode.s},${episode.e}`
    return acc
  },
  {},
)

const getPlaylistChapters = (videos: Array<Video>, episodes: Episodes) => {
  const groupedByPeriods = groupBy(videos, ({ period }) => period)
  return reduce(
    episodes,
    (acc: Chapters, episode) => {
      const video = groupedByPeriods[episode.h]?.[0]
      if (!video || episode.s >= episode.e) return acc

      const episodeDuration = (episode.e - episode.s) * 1000
      const prevVideoEndMs = last(acc)?.endMs || 0
      const nextChapter = {
        duration: episodeDuration,
        endMs: prevVideoEndMs + episodeDuration,
        endOffsetMs: episode.e * 1000,
        period: video.period,
        startMs: prevVideoEndMs,
        startOffsetMs: episode.s * 1000,
        urls: getEpisodeUrls(video.urls, episode),
      }
      return concat(acc, nextChapter)
    },
    [],
  )
}

type Args = {
  episodes: Episodes,
  selectedPlaylist?: PlaylistOption,
  videos: Videos,
}

export const buildChapters = ({
  episodes,
  selectedPlaylist,
  videos,
}: Args) => {
  const periods = getUniquePeriods(videos)
  const highQualityVideos = getVideoByPeriods(videos, periods)
  return selectedPlaylist?.id === FULL_GAME_KEY
    ? getFullMatchChapters(highQualityVideos)
    : getPlaylistChapters(highQualityVideos, episodes)
}
