package database

import (
	"fmt"
	"io/fs"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

var wg sync.WaitGroup

func SyncGames() {
	start := time.Now()
	dir := os.Getenv("MUSIC_PATH")
	fmt.Printf("dir: %s\n", dir)
	foldersToSkip := []string{".sync", "dist", "old", "characters"}
	fmt.Println(foldersToSkip)
	SetGameDeletionDate()
	checkBrokenSongs()

	files, err := os.ReadDir(dir)
	if err != nil {
		log.Fatal(err)
	}

	for _, file := range files {
		syncGame(file, foldersToSkip, dir)
	}
	finished := time.Now()
	totalTime := finished.Sub(start)
	out := time.Time{}.Add(totalTime)
	fmt.Printf("\nTotal time: %v\n", totalTime)
	fmt.Printf("Total time: %v\n", out.Format("15:04:05.00000"))
}

func SyncGamesQuick() {
	start := time.Now()
	dir := os.Getenv("MUSIC_PATH")
	fmt.Printf("dir: %s\n", dir)
	foldersToSkip := []string{".sync", "dist", "old", "characters"}
	fmt.Println(foldersToSkip)
	SetGameDeletionDate()
	checkBrokenSongs()

	files, err := os.ReadDir(dir)
	if err != nil {
		log.Fatal(err)
	}

	for _, file := range files {
		wg.Add(1)
		go func() {
			defer wg.Done()
			syncGame(file, foldersToSkip, dir)
		}()
	}
	wg.Wait()
	finished := time.Now()
	totalTime := finished.Sub(start)
	out := time.Time{}.Add(totalTime)
	fmt.Printf("\nTotal time: %v\n", totalTime)
	fmt.Printf("Total time: %v\n", out.Format("15:04:05.00000"))
}

func syncGame(file os.DirEntry, foldersToSkip []string, dir string) {
	if file.IsDir() && !contains(foldersToSkip, file.Name()) {
		fmt.Println(file.Name())
		path := dir + file.Name() + "/"
		fmt.Println(path)

		entries, err := os.ReadDir(path)
		if err != nil {
			log.Println(err)
		}
		id := -1
		for _, entry := range entries {
			fileInfo, err := entry.Info()
			if err != nil {
				log.Println(err)
			}
			id = getIdFromFile(fileInfo)
			if id != -1 {
				break
			}
		}
		if id == -1 {
			addNewGame(file.Name(), path)
		} else {
			checkIfChanged(id, file.Name(), path)
			checkSongs(path, id)
		}
	}
}

func getIdFromFile(file os.FileInfo) int {
	name := file.Name()
	if !file.IsDir() && strings.HasSuffix(name, ".id") {
		name = strings.Replace(name, ".id", "", 1)
		name = strings.Replace(name, ".", "", 1)
		i, _ := strconv.Atoi(name)
		return i
	}
	return -1
}

func checkIfChanged(id int, name string, path string) {
	fmt.Printf("Id from file: %v\n", id)
	nameFromDb := GetGameName(id)
	fmt.Printf("Name from file: %v\n", name)
	fmt.Printf("Name from DB: %v\n", nameFromDb)
	if nameFromDb == "" {
		fmt.Println("Not in db")
		InsertGameWithExistingId(id, name, path)
		fmt.Println("Added to db")
	} else if name != nameFromDb {
		fmt.Println("Diff name")
		UpdateGameName(id, name, path)
		checkBrokenSongs()
	}
	RemoveDeletionDate(id)
}

func addNewGame(name string, path string) {
	newId := GetIdByGameName(name)
	if newId != -1 {
		checkBrokenSongs()
		RemoveDeletionDate(newId)
	} else {
		newId = InsertGame(name, path)
	}

	fmt.Printf("newId = %v", newId)
	fileName := path + "/." + strconv.Itoa(newId) + ".id"
	fmt.Printf("fileName = %v", fileName)

	err := os.WriteFile(fileName, nil, 0644)
	if err != nil {
		panic(err)
	}
	checkSongs(path, newId)
}

func checkSongs(gameDir string, gameId int) {
	files, err := os.ReadDir(gameDir)
	if err != nil {
		log.Println(err)
	}
	for _, file := range files {
		entry, err := file.Info()
		if err != nil {
			log.Println(err)
		}
		if isSong(entry) {
			path := gameDir + entry.Name()
			fileName := entry.Name()
			songName, _ := strings.CutSuffix(fileName, ".mp3")
			if CheckSong(path) {
				UpdateSong(songName, fileName, path)
			} else {
				AddSong(SongData{GameId: gameId, SongName: songName, Path: path, FileName: fileName})
			}
		} else if isCoverImage(entry) {
			//TODO: Later add cover art image here in db
		}
	}
	//TODO: Add number of songs here
}

func checkBrokenSongs() {
	allSongs := FetchAllSongs()
	var brokenSongs []SongData
	for _, song := range allSongs {
		//Check if file exists and open
		openFile, err := os.Open(song.Path)
		if err != nil {
			//File not found
			brokenSongs = append(brokenSongs, song)
			fmt.Printf("song broken: %v", song.Path)
		} else {
			err = openFile.Close()
			if err != nil {
				log.Println(err)
			}
		}
	}
	RemoveBrokenSongs(brokenSongs)
}

func isSong(entry fs.FileInfo) bool {
	return !entry.IsDir() && strings.HasSuffix(entry.Name(), ".mp3")
}

func isCoverImage(entry fs.FileInfo) bool {
	return !entry.IsDir() && strings.Contains(entry.Name(), "cover") &&
		(strings.HasSuffix(entry.Name(), ".jpg") || strings.HasSuffix(entry.Name(), ".png"))
}

func contains(s []string, searchTerm string) bool {
	i := sort.SearchStrings(s, searchTerm)
	return i < len(s) && s[i] == searchTerm
}
