package backend

import (
	"log"
	"math/rand"
	"music-server/internal/db"
	"music-server/internal/db/repository"
	"os"
	"strconv"
	"strings"
)

type SongInfo struct {
	Game             string `json:"Game"`
	GamePlayed       int32  `json:"GamePlayed"`
	Song             string `json:"Song"`
	SongPlayed       int32  `json:"SongPlayed"`
	CurrentlyPlaying bool   `json:"CurrentlyPlaying"`
	SongNo           int    `json:"SongNo"`
}

var currentSong = -1

// var games []models.GameData
var gamesNew []repository.Game

// var songQue []models.SongData
var songQueNew []repository.Song

// var lastFetched models.SongData
var lastFetchedNew repository.Song
var repo *repository.Queries

func initRepo() {
	if repo == nil {
		repo = repository.New(db.Dbpool)
	}
}

func getAllGames() []repository.Game {
	if len(gamesNew) == 0 {
		initRepo()
		gamesNew, _ = repo.FindAllGames(db.Ctx)
	}
	return gamesNew

}

func GetSoundCheckSong() string {
	files, err := os.ReadDir("songs")
	if err != nil {
		log.Fatal(err)
	}
	fileInfo := files[rand.Intn(len(files))]
	return "songs/" + fileInfo.Name()
}

func Reset() {
	songQueNew = nil
	currentSong = -1
	initRepo()
	gamesNew, _ = repo.FindAllGames(db.Ctx)
	//games = db.FindAllGames()
}

func AddLatestToQue() {
	if lastFetchedNew.Path != "" {
		currentSong = len(songQueNew)
		songQueNew = append(songQueNew, lastFetchedNew)
		lastFetchedNew = repository.Song{}
	}
}

func AddLatestPlayed() {
	if len(songQueNew) == 0 {
		return
	}
	currentSongData := songQueNew[currentSong]

	initRepo()
	repo.AddGamePlayed(db.Ctx, currentSongData.GameID)
	repo.AddSongPlayed(db.Ctx, repository.AddSongPlayedParams{GameID: currentSongData.GameID, SongName: currentSongData.SongName})
	//db.AddGamePlayed(currentSongData.GameId)
	//db.AddSongPlayed(currentSongData.GameId, currentSongData.SongName)
}

func SetPlayed(songNumber int) {
	if len(songQueNew) == 0 || songNumber >= len(songQueNew) {
		return
	}
	songData := songQueNew[songNumber]
	initRepo()
	repo.AddGamePlayed(db.Ctx, songData.GameID)
	repo.AddSongPlayed(db.Ctx, repository.AddSongPlayedParams{GameID: songData.GameID, SongName: songData.SongName})
	//db.AddGamePlayed(songData.GameId)
	//db.AddSongPlayed(songData.GameId, songData.SongName)
}

func GetRandomSong() string {
	/*if len(games) == 0 {
		games = db.FindAllGames()
	}*/
	getAllGames()
	if len(gamesNew) == 0 {
		return ""
	}

	song := getSongFromList(gamesNew)
	lastFetchedNew = song
	return song.Path
}

func GetRandomSongLowChance() string {
	/*if len(games) == 0 {
		games = db.FindAllGames()
	}*/
	getAllGames()

	//var listOfGames []models.GameData
	var listOfGames []repository.Game

	var averagePlayed = getAveragePlayed()

	for _, data := range gamesNew {
		timesToAdd := averagePlayed - data.TimesPlayed
		if timesToAdd <= 0 {
			listOfGames = append(listOfGames, data)
		} else {
			for i := int32(0); i < timesToAdd; i++ {
				listOfGames = append(listOfGames, data)
			}
		}
	}
	song := getSongFromList(listOfGames)

	lastFetchedNew = song
	return song.Path

}

func GetRandomSongClassic() string {
	/*if games == nil || len(games) == 0 {
		games = db.FindAllGames()
	}*/

	getAllGames()

	var listOfAllSongs []repository.Song
	for _, game := range gamesNew {
		//listOfAllSongs = append(listOfAllSongs, db.FindSongsFromGame(game.Id)...)
		songList, _ := repo.FindSongsFromGame(db.Ctx, game.ID)
		listOfAllSongs = append(listOfAllSongs, songList...)
	}

	songFound := false
	var song repository.Song
	for !songFound {
		song = listOfAllSongs[rand.Intn(len(listOfAllSongs))]
		//gameData, err := db.GetGameById(song.GameId)
		gameData, err := repo.GetGameById(db.Ctx, song.GameID)

		if err != nil {
			//db.RemoveBrokenSong(song)
			repo.RemoveBrokenSong(db.Ctx, song.Path)
			//log.Println("Song not found, song '" + song.SongName + "' deleted from game '" + gameData.GameName + "' FileName: " + song.FileName)
			log.Printf("Song not found, song '%s' deleted from game '%s' FileName: %v\n", song.SongName, gameData.GameName, song.FileName)
			continue
		}

		//Check if file exists and open
		openFile, err := os.Open(song.Path)
		if err != nil || (song.FileName != nil && gameData.Path+*song.FileName != song.Path) {
			//File not found
			//db.RemoveBrokenSong(song)
			repo.RemoveBrokenSong(db.Ctx, song.Path)
			//log.Println("Song not found, song '" + song.SongName + "' deleted from game '" + gameData.GameName + "' FileName: " + song.FileName)
			log.Printf("Song not found, song '%s' deleted from game '%s' FileName: %v\n", song.SongName, gameData.GameName, song.FileName)
		} else {
			songFound = true
		}
		err = openFile.Close()
		if err != nil {
			log.Println(err)
		}
	}
	lastFetchedNew = song
	return song.Path
}

func GetSongInfo() SongInfo {
	if songQueNew == nil {
		return SongInfo{}
	}
	var currentSongData = songQueNew[currentSong]

	currentGameData := getCurrentGame(currentSongData)

	return SongInfo{
		Game:             currentGameData.GameName,
		GamePlayed:       currentGameData.TimesPlayed,
		Song:             currentSongData.SongName,
		SongPlayed:       currentSongData.TimesPlayed,
		CurrentlyPlaying: true,
		SongNo:           currentSong,
	}
}

func GetPlayedSongs() []SongInfo {
	var songList []SongInfo

	for i, song := range songQueNew {
		gameData := getCurrentGame(song)
		songList = append(songList, SongInfo{
			Game:             gameData.GameName,
			GamePlayed:       gameData.TimesPlayed,
			Song:             song.SongName,
			SongPlayed:       song.TimesPlayed,
			CurrentlyPlaying: i == currentSong,
			SongNo:           i,
		})
	}
	return songList
}

func GetSong(song string) string {
	currentSong, _ = strconv.Atoi(song)
	if currentSong >= len(songQueNew) {
		currentSong = len(songQueNew) - 1
	} else if currentSong < 0 {
		currentSong = 0
	}
	songData := songQueNew[currentSong]
	return songData.Path
}

func GetAllGames() []string {
	/*if games == nil || len(games) == 0 {
		games = db.FindAllGames()
	}*/
	getAllGames()

	var jsonArray []string
	for _, game := range gamesNew {
		jsonArray = append(jsonArray, game.GameName)
	}
	return jsonArray
}

func GetAllGamesRandom() []string {
	/*if games == nil || len(games) == 0 {
		games = db.FindAllGames()
	}*/
	getAllGames()

	var jsonArray []string
	for _, game := range gamesNew {
		jsonArray = append(jsonArray, game.GameName)
	}
	rand.Shuffle(len(jsonArray), func(i, j int) { jsonArray[i], jsonArray[j] = jsonArray[j], jsonArray[i] })
	return jsonArray
}

func GetNextSong() string {
	if songQueNew == nil {
		return ""
	}
	if currentSong == len(songQueNew)-1 || currentSong == -1 {
		songData := songQueNew[currentSong]
		return songData.Path
	} else {
		currentSong = currentSong + 1
		songData := songQueNew[currentSong]
		return songData.Path
	}
}

func GetPreviousSong() string {
	if songQueNew == nil {
		return ""
	}
	if currentSong == -1 || currentSong == 0 {
		songData := songQueNew[0]
		return songData.Path
	} else {
		currentSong = currentSong - 1
		songData := songQueNew[currentSong]
		return songData.Path
	}
}

func getSongFromList(games []repository.Game) repository.Song {
	songFound := false
	var song repository.Song
	for !songFound {
		game := getRandomGame(games)
		//log.Println("game = ", game)
		//songs := db.FindSongsFromGame(game.Id)
		songs, _ := repo.FindSongsFromGame(db.Ctx, game.ID)
		//log.Println("songs = ", songs)
		if len(songs) == 0 {
			continue
		}
		song = songs[rand.Intn(len(songs))]
		log.Println("song = ", song)

		//Check if file exists and open
		openFile, err := os.Open(song.Path)
		//log.Println("game.Path+song.FileName: ", game.Path+song.FileName)
		//log.Println("song.Path: ", song.Path)
		//log.Println("game.Path+song.FileName != song.Path: ", game.Path+song.FileName != song.Path)
		if err != nil || (song.FileName != nil && game.Path+*song.FileName != song.Path) || (song.FileName != nil && strings.HasSuffix(*song.FileName, ".wav")) {
			//File not found
			//db.RemoveBrokenSong(song)
			repo.RemoveBrokenSong(db.Ctx, song.Path)
			//log.Println("Song not found, song '" + song.SongName + "' deleted from game '" + game.GameName + "' FileName: " + song.FileName)
			log.Printf("Song not found, song '%s' deleted from game '%s' FileName: %v\n", song.SongName, game.GameName, song.FileName)
		} else {
			songFound = true
		}

		err = openFile.Close()
		if err != nil {
			log.Println(err)
		}
	}
	return song
}

func getCurrentGame(currentSongData repository.Song) repository.Game {
	for _, game := range gamesNew {
		if game.ID == currentSongData.GameID {
			return game
		}
	}
	return repository.Game{}
}

func getAveragePlayed() int32 {
	getAllGames()
	var sum int32
	for _, data := range gamesNew {
		sum += data.TimesPlayed
	}
	return sum / int32(len(gamesNew))
}

func getRandomGame(listOfGames []repository.Game) repository.Game {
	return listOfGames[rand.Intn(len(listOfGames))]
}
