package internal

import (
	"errors"
	"strings"

	"coopcloud.tech/abra/pkg/app"
	"coopcloud.tech/abra/pkg/config"
	"coopcloud.tech/abra/pkg/log"
	"coopcloud.tech/abra/pkg/recipe"
	"github.com/AlecAivazis/survey/v2"
	"github.com/urfave/cli/v3"
)

// ValidateRecipe ensures the recipe arg is valid.
func ValidateRecipe(cmd *cli.Command) recipe.Recipe {
	recipeName := cmd.Args().First()

	if recipeName == "" && !NoInput {
		var recipes []string

		catl, err := recipe.ReadRecipeCatalogue(Offline)
		if err != nil {
			log.Fatal(err)
		}

		knownRecipes := make(map[string]bool)
		for name := range catl {
			knownRecipes[name] = true
		}

		localRecipes, err := recipe.GetRecipesLocal()
		if err != nil {
			log.Fatal(err)
		}

		for _, recipeLocal := range localRecipes {
			if _, ok := knownRecipes[recipeLocal]; !ok {
				knownRecipes[recipeLocal] = true
			}
		}

		for recipeName := range knownRecipes {
			recipes = append(recipes, recipeName)
		}

		prompt := &survey.Select{
			Message: "Select recipe",
			Options: recipes,
		}
		if err := survey.AskOne(prompt, &recipeName); err != nil {
			log.Fatal(err)
		}
	}

	if recipeName == "" {
		ShowSubcommandHelpAndError(cmd, errors.New("no recipe name provided"))
	}

	chosenRecipe := recipe.Get(recipeName)
	err := chosenRecipe.EnsureExists()
	if err != nil {
		log.Fatal(err)
	}
	_, err = chosenRecipe.GetComposeConfig(nil)
	if err != nil {
		if cmd.Name == "generate" {
			if strings.Contains(err.Error(), "missing a compose") {
				log.Fatal(err)
			}
			log.Warn(err)
		} else {
			if strings.Contains(err.Error(), "template_driver is not allowed") {
				log.Warnf("ensure %s recipe compose.* files include \"version: '3.8'\"", recipeName)
			}
			log.Fatalf("unable to validate recipe: %s", err)
		}
	}

	log.Debugf("validated %s as recipe argument", recipeName)

	return chosenRecipe
}

// ValidateApp ensures the app name arg is valid.
func ValidateApp(cmd *cli.Command) app.App {
	appName := cmd.Args().First()

	if appName == "" {
		ShowSubcommandHelpAndError(cmd, errors.New("no app provided"))
	}

	app, err := app.Get(appName)
	if err != nil {
		log.Fatal(err)
	}

	log.Debugf("validated %s as app argument", appName)

	return app
}

// ValidateDomain ensures the domain name arg is valid.
func ValidateDomain(cmd *cli.Command) string {
	domainName := cmd.Args().First()

	if domainName == "" && !NoInput {
		prompt := &survey.Input{
			Message: "Specify a domain name",
			Default: "example.com",
		}
		if err := survey.AskOne(prompt, &domainName); err != nil {
			log.Fatal(err)
		}
	}

	if domainName == "" {
		ShowSubcommandHelpAndError(cmd, errors.New("no domain provided"))
	}

	log.Debugf("validated %s as domain argument", domainName)

	return domainName
}

// ValidateSubCmdFlags ensures flag order conforms to correct order
func ValidateSubCmdFlags(cmd *cli.Command) bool {
	for argIdx, arg := range cmd.Args().Slice() {
		if !strings.HasPrefix(arg, "--") {
			for _, flag := range cmd.Args().Slice()[argIdx:] {
				if strings.HasPrefix(flag, "--") {
					return false
				}
			}
		}
	}
	return true
}

// ValidateServer ensures the server name arg is valid.
func ValidateServer(cmd *cli.Command) string {
	serverName := cmd.Args().First()

	serverNames, err := config.ReadServerNames()
	if err != nil {
		log.Fatal(err)
	}

	if serverName == "" && !NoInput {
		prompt := &survey.Select{
			Message: "Specify a server name",
			Options: serverNames,
		}
		if err := survey.AskOne(prompt, &serverName); err != nil {
			log.Fatal(err)
		}
	}

	matched := false
	for _, name := range serverNames {
		if name == serverName {
			matched = true
		}
	}

	if serverName == "" {
		ShowSubcommandHelpAndError(cmd, errors.New("no server provided"))
	}

	if !matched {
		ShowSubcommandHelpAndError(cmd, errors.New("server doesn't exist?"))
	}

	log.Debugf("validated %s as server argument", serverName)

	return serverName
}