package settings

import (
	"fmt"
	"github.com/packwiz/packwiz/cmdshared"
	"github.com/packwiz/packwiz/core"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/unascribed/FlexVer/go/flexver"
	"golang.org/x/exp/slices"
	"os"
	"strings"
)

var acceptableVersionsCommand = &cobra.Command{
	Use:     "acceptable-versions",
	Short:   "Manage your pack's acceptable Minecraft versions. This must be a comma seperated list of Minecraft versions, e.g. 1.16.3,1.16.4,1.16.5",
	Aliases: []string{"av"},
	Args:    cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		modpack, err := core.LoadPack()
		if err != nil {
			// Check if it's a no such file or directory error
			if os.IsNotExist(err) {
				fmt.Println("No pack.toml file found, run 'packwiz init' to create one!")
				os.Exit(1)
			}
			fmt.Printf("Error loading pack: %s\n", err)
			os.Exit(1)
		}
		var currentVersions []string
		// Check if they have no options whatsoever
		if modpack.Options == nil {
			// Initialize the options
			modpack.Options = make(map[string]interface{})
		}
		// Check if the acceptable-game-versions is nil, which would mean their pack.toml doesn't have it set yet
		if modpack.Options["acceptable-game-versions"] != nil {
			// Convert the interface{} to a string slice
			for _, v := range modpack.Options["acceptable-game-versions"].([]interface{}) {
				currentVersions = append(currentVersions, v.(string))
			}
		}
		// Check our flags to see if we're adding or removing
		if viper.GetBool("settings.acceptable-versions.add") {
			// Adding
			acceptableVersion := args[0]
			// Check if the version is already in the list
			if slices.Contains(currentVersions, acceptableVersion) {
				fmt.Printf("Version %s is already in your acceptable versions list!\n", acceptableVersion)
				os.Exit(1)
			}
			// Add the version to the list and re-sort it
			currentVersions = append(currentVersions, acceptableVersion)
			flexver.VersionSlice(currentVersions).Sort()
			// Set the new list
			modpack.Options["acceptable-game-versions"] = currentVersions
			// Save the pack
			err = modpack.Write()
			if err != nil {
				fmt.Printf("Error writing pack: %s\n", err)
				os.Exit(1)
			}
			// Print success message
			prettyList := strings.Join(currentVersions, ", ")
			fmt.Printf("Added %s to acceptable versions list, now %s\n", acceptableVersion, prettyList)
		} else if viper.GetBool("settings.acceptable-versions.remove") {
			// Removing
			acceptableVersion := args[0]
			// Check if the version is in the list
			if !slices.Contains(currentVersions, acceptableVersion) {
				fmt.Printf("Version %s is not in your acceptable versions list!\n", acceptableVersion)
				os.Exit(1)
			}
			// Remove the version from the list
			i := slices.Index(currentVersions, acceptableVersion)
			currentVersions = slices.Delete(currentVersions, i, i+1)
			// Sort it just in case it's out of order
			flexver.VersionSlice(currentVersions).Sort()
			// Set the new list
			modpack.Options["acceptable-game-versions"] = currentVersions
			// Save the pack
			err = modpack.Write()
			if err != nil {
				fmt.Printf("Error writing pack: %s\n", err)
				os.Exit(1)
			}
			// Print success message
			prettyList := strings.Join(currentVersions, ", ")
			fmt.Printf("Removed %s from acceptable versions list, now %s\n", acceptableVersion, prettyList)
		} else {
			// Overwriting
			acceptableVersions := args[0]
			acceptableVersionsList := strings.Split(acceptableVersions, ",")
			// Dedupe the list
			acceptableVersionsDeduped := []string(nil)
			for i, v := range acceptableVersionsList {
				if !slices.Contains(acceptableVersionsList[i+1:], v) {
					acceptableVersionsDeduped = append(acceptableVersionsDeduped, v)
				}
			}
			// Check if the list of versions is out of order, lowest to highest, and inform the user if it is
			// Compare the versions one by one to the next one, if the next one is lower, then it's out of order
			// If it's only 1 element long, then it's already sorted
			if len(acceptableVersionsDeduped) > 1 {
				for i, v := range acceptableVersionsDeduped {
					if flexver.Less(acceptableVersionsDeduped[i+1], v) {
						fmt.Printf("Warning: Your acceptable versions list is out of order. ")
						// Give a do you mean example
						// Clone the list
						acceptableVersionsDedupedClone := make([]string, len(acceptableVersionsDeduped))
						copy(acceptableVersionsDedupedClone, acceptableVersionsDeduped)
						flexver.VersionSlice(acceptableVersionsDedupedClone).Sort()
						fmt.Printf("Did you mean %s?\n", strings.Join(acceptableVersionsDedupedClone, ", "))
						if cmdshared.PromptYesNo("Would you like to fix this automatically? [Y/n] ") {
							// If yes we'll just set the list to the sorted one
							acceptableVersionsDeduped = acceptableVersionsDedupedClone
							break
						} else {
							// If no we'll just continue
							break
						}
					}
				}
			}
			modpack.Options["acceptable-game-versions"] = acceptableVersionsDeduped
			err = modpack.Write()
			if err != nil {
				fmt.Printf("Error writing pack: %s\n", err)
				os.Exit(1)
			}
			// Print success message
			prettyList := strings.Join(acceptableVersionsDeduped, ", ")
			fmt.Printf("Set acceptable versions to %s\n", prettyList)
		}
	},
}

func init() {
	settingsCmd.AddCommand(acceptableVersionsCommand)

	// Add and remove flags for adding or removing specific versions
	acceptableVersionsCommand.Flags().BoolP("add", "a", false, "Add a version to the list")
	acceptableVersionsCommand.Flags().BoolP("remove", "r", false, "Remove a version from the list")
	_ = viper.BindPFlag("settings.acceptable-versions.add", acceptableVersionsCommand.Flags().Lookup("add"))
	_ = viper.BindPFlag("settings.acceptable-versions.remove", acceptableVersionsCommand.Flags().Lookup("remove"))
}