import Jax from './Jax'
import { MatchlistDto } from './Jax/src/Endpoints/MatchlistEndpoint'
import { SummonerDTO } from './Jax/src/Endpoints/SummonerEndpoint'
import Summoner from 'App/Models/Summoner'
import Database from '@ioc:Adonis/Lucid/Database'
import MatchParser from 'App/Parsers/MatchParser'
import BasicMatchSerializer from 'App/Serializers/BasicMatchSerializer'
import { SerializedMatch } from 'App/Serializers/SerializedTypes'
import Match from 'App/Models/Match'
import { notEmpty, tutorialQueues } from 'App/helpers'

class MatchService {
  /**
   * Add 100 matches at a time to MatchList until the stopFetching condition is true
   * @param account of the summoner
   * @param region of the summoner
   * @param stopFetching condition to stop fetching the MatchList
   */
  private async _fetchMatchListUntil(account: SummonerDTO, region: string, stopFetching: any) {
    let matchList: MatchlistDto = []
    let alreadyIn = false
    let index = 0
    do {
      console.log('--> CALL TO RIOT MATCHLIST')
      const newMatchList = await Jax.Matchlist.puuid(account.puuid, region, index)
      // Error while fetching Riot API
      if (!newMatchList) {
        return matchList
      }
      matchList = [...matchList, ...newMatchList]
      alreadyIn = newMatchList.length === 0 || stopFetching(newMatchList)
      // If the match is made in another region : we stop fetching
      if (matchList[matchList.length - 1].split('_')[0].toLowerCase() !== region.toLowerCase()) {
        alreadyIn = true
      }
      index += 100
    } while (!alreadyIn)
    return matchList
  }
  /**
   * Update the full MatchList of the summoner
   */
  public async updateMatchList(
    account: SummonerDTO,
    region: string,
    summonerDB: Summoner
  ): Promise<MatchlistDto> {
    console.time('matchList')

    const currentMatchList = await summonerDB.related('matchList').query().orderBy('matchId', 'asc')
    const currentMatchListIds = currentMatchList.map((m) => m.matchId)

    console.time('RiotMatchList')
    const newMatchList = await this._fetchMatchListUntil(
      account,
      region,
      (newMatchList: MatchlistDto) => {
        return currentMatchListIds.some((id) => id === newMatchList[newMatchList.length - 1])
      }
    )
    console.timeEnd('RiotMatchList')

    const matchListToSave: MatchlistDto = []
    for (const matchId of newMatchList.reverse()) {
      if (!currentMatchListIds.some((id) => id === matchId)) {
        matchListToSave.push(matchId)
        currentMatchListIds.push(matchId)
      }
    }

    // If there is new matchIds to save in database
    if (matchListToSave.length) {
      await Database.table('summoner_matchlist').multiInsert(
        matchListToSave.map((id) => ({
          match_id: id,
          summoner_puuid: summonerDB.puuid,
        }))
      )
    }

    console.timeEnd('matchList')
    return currentMatchListIds.reverse()
  }

  /**
   * Fetch list of matches for a specific Summoner
   */
  public async getMatches(
    region: string,
    matchIds: string[],
    puuid: string
  ): Promise<SerializedMatch[]> {
    console.time('getMatches')

    const matches: SerializedMatch[] = []
    const matchesToGetFromRiot: MatchlistDto = []
    for (let i = 0; i < matchIds.length; ++i) {
      const matchSaved = await Match.query()
        .where('id', matchIds[i])
        .preload('teams')
        .preload('players')
        .first()

      if (matchSaved) {
        // TODO: Serialize match from DB + put it in Redis + push it in "matches"
        matches.push(BasicMatchSerializer.serializeOneMatch(matchSaved, puuid))
      } else {
        matchesToGetFromRiot.push(matchIds[i])
      }
    }

    const requests = matchesToGetFromRiot.map((gameId) => Jax.Match.get(gameId, region))
    const matchesFromApi = await Promise.all(requests)

    /* If we have to store some matches in the db */
    if (matchesFromApi.length !== 0) {
      // Remove bugged matches from the Riot API + tutorial games
      const filteredMatches = matchesFromApi
        .filter(notEmpty)
        .filter(
          (m) =>
            !tutorialQueues.includes(m.info.queueId) &&
            m.info.teams.length > 0 &&
            m.info.participants.length > 0
        )

      // Transform raw matches data
      const parsedMatches: any = await MatchParser.parse(filteredMatches)

      // TODO: Serialize match from DB + put it in Redis + push it in "matches"
      const serializedMatches = BasicMatchSerializer.serialize(parsedMatches, puuid, true)
      matches.push(...serializedMatches)
    }

    // Todo: check if we need to sort here
    matches.sort((a, b) => (a.date < b.date ? 1 : -1))
    console.timeEnd('getMatches')
    return matches
  }
}

export default new MatchService()
