import type { Dispatch, SetStateAction } from 'react'
import {
  useMemo,
  useEffect,
  useState,
  useCallback,
} from 'react'
import { useQueryClient } from 'react-query'

import throttle from 'lodash/throttle'
import isEmpty from 'lodash/isEmpty'
import includes from 'lodash/includes'
import find from 'lodash/find'
import isUndefined from 'lodash/isUndefined'
import flatMapDepth from 'lodash/flatMapDepth'
import uniqBy from 'lodash/uniqBy'
import values from 'lodash/values'
import size from 'lodash/size'

import { querieKeys } from 'config'

import type {
  DataItem,
  MatchScore,
  PlayerParam,
} from 'requests'
import {
  MatchInfo,
  PlayersStats,
  Player,
  getPlayersStats,
  getMatchParticipants,
} from 'requests'

import { getLocalStorageItem } from 'helpers/getLocalStorage'

import { useObjectState, usePageParams } from 'hooks'

import { StatsType, Tabs as StatsTabs } from 'features/MatchSidePlaylists/components/TabStats/config'
import { getHalfTime } from 'features/MatchPage/helpers/getHalfTime'
import { TOUR_COMPLETED_STORAGE_KEY } from 'features/MatchTour'
import { Tabs } from 'features/MatchSidePlaylists/config'

import { DISPLAYED_PARAMS_COLUMNS } from 'features/MatchSidePlaylists/components/PlayersTable/config'
import { useFakeData } from './useFakeData'

const REQUEST_DELAY = 5000
const STATS_POLL_INTERVAL = 30000

type UsePlayersStatsArgs = {
  matchProfile: MatchInfo,
  playingProgress: number,
  selectedStatsTable: StatsTabs,
  selectedTab: Tabs,
  setIsPlayersStatsFetching: Dispatch<SetStateAction<boolean>>,
  statsType: StatsType,
}

type PlayersData = {
  team1: Array<Player>,
  team2: Array<Player>,
}

export const usePlayersStats = ({
  matchProfile,
  playingProgress,
  selectedStatsTable,
  selectedTab,
  setIsPlayersStatsFetching,
  statsType,
}: UsePlayersStatsArgs) => {
  const [playersStats, setPlayersStats] = useObjectState<Record<string, PlayersStats>>({})
  const [playersData, setPlayersData] = useState<PlayersData>({ team1: [], team2: [] })

  const {
    profileId: matchId,
    sportName,
    sportType,
  } = usePageParams()

  const fakeData = useFakeData(matchProfile)

  const client = useQueryClient()

  const matchScore = client.getQueryData<MatchScore>(querieKeys.matchScore)

  const isCurrentStats = statsType === StatsType.CURRENT_STATS

  const getParams = useCallback((stats: PlayersStats) => (
    uniqBy(flatMapDepth(stats, values), 'id') as Array<PlayerParam>
  ), [])

  const fetchPlayers = useMemo(() => throttle(async (second?: number) => {
    const videoBounds = matchScore?.video_bounds || matchProfile?.video_bounds

    if (
      !matchProfile?.team1.id
        || !matchProfile?.team2.id
        || !videoBounds
    ) return null

    try {
      return getMatchParticipants({
        matchId,
        sportType,
        ...(!isUndefined(second) && getHalfTime(videoBounds, second)),
      })
    } catch (e) {
      return Promise.reject(e)
    }
  }, REQUEST_DELAY), [
    matchId,
    matchProfile?.team1.id,
    matchProfile?.team2.id,
    matchProfile?.video_bounds,
    matchScore?.video_bounds,
    sportType,
  ])

  const fetchPlayersStats = useMemo(() => (async (team: 'team1' | 'team2', second?: number) => {
    const videoBounds = matchScore?.video_bounds || matchProfile?.video_bounds

    if (!sportName || !matchProfile?.[team].id || !videoBounds) return null

    try {
      return getPlayersStats({
        matchId,
        sportName,
        teamId: matchProfile[team].id,
        ...(!isUndefined(second) && getHalfTime(videoBounds, second)),
      })
    } catch (e) {
      return Promise.reject(e)
    }
  }), [
    matchScore?.video_bounds,
    matchProfile,
    sportName,
    matchId,
  ])

  const fetchData = useMemo(() => throttle(async (second?: number) => {
    const isTeam1Selected = selectedStatsTable === StatsTabs.TEAM1

    if (
      selectedTab !== Tabs.STATS
       || !includes([StatsTabs.TEAM1, StatsTabs.TEAM2], selectedStatsTable)
       || !matchProfile?.team1.id
       || !matchProfile?.team2.id
       || (!matchProfile?.live && !isCurrentStats && !isEmpty(playersStats[isTeam1Selected
         ? matchProfile.team1.id
         : matchProfile.team2.id]))
    ) return

    const [res1, res2] = await Promise.all([
      fetchPlayers(second),
      isTeam1Selected
        ? fetchPlayersStats('team1', second)
        : fetchPlayersStats('team2', second),
    ]) as [Array<DataItem> | null, PlayersStats | null]

    const team1Players = find(res1, { team_id: matchProfile?.team1.id })?.players || []
    const team2Players = find(res1, { team_id: matchProfile?.team2.id })?.players || []

    const needUseFakeData = getLocalStorageItem(TOUR_COMPLETED_STORAGE_KEY) !== 'true'
      && (
        isEmpty(team1Players)
          || isEmpty(res2)
          || size(getParams(res2)) <= DISPLAYED_PARAMS_COLUMNS
      )

    setPlayersData({
      team1: needUseFakeData ? fakeData.playersData[0].players : team1Players,
      team2: needUseFakeData ? fakeData.playersData[1].players : team2Players,
    })

    setPlayersStats({
      ...(isTeam1Selected && res2 && {
        [matchProfile.team1.id]: needUseFakeData
          ? fakeData.playersStats as unknown as PlayersStats
          : res2,
      }),
      ...(!isTeam1Selected && res2 && {
        [matchProfile.team2.id]: needUseFakeData
          ? fakeData.playersStats as unknown as PlayersStats
          : res2,
      }),
    })

    setIsPlayersStatsFetching(false)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, REQUEST_DELAY), [
    fetchPlayers,
    fetchPlayersStats,
    setPlayersStats,
    matchProfile?.team1.id,
    matchProfile?.team2.id,
    matchProfile?.live,
    setIsPlayersStatsFetching,
    getParams,
    fakeData,
    selectedTab,
    selectedStatsTable,
    isCurrentStats,
  ])

  const beforeCloseTourCallback = () => {
    isCurrentStats ? fetchData(playingProgress) : fetchData()
  }

  useEffect(() => {
    let interval: NodeJS.Timeout

    if (!isCurrentStats) {
      fetchData()
    }

    if (matchProfile?.live && !isCurrentStats) {
      interval = setInterval(() => {
        fetchData()
      }, STATS_POLL_INTERVAL)
    }

    return () => clearInterval(interval)
  }, [
    fetchData,
    isCurrentStats,
    matchProfile?.live,
  ])

  useEffect(() => {
    if (isCurrentStats) {
      fetchData(playingProgress)
    }
  }, [
    fetchData,
    playingProgress,
    isCurrentStats,
    matchProfile?.live,
  ])

  return {
    beforeCloseTourCallback,
    getParams,
    playersData,
    playersStats,
  }
}
