package cli

import (
	"fmt"
	"os"

	"coopcloud.tech/abra/cli/app"
	"coopcloud.tech/abra/cli/catalogue"
	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/cli/recipe"
	"coopcloud.tech/abra/cli/server"
	"coopcloud.tech/abra/pkg/config"
	"coopcloud.tech/abra/pkg/log"
	charmLog "github.com/charmbracelet/log"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
)

func Run(version, commit string) {
	rootCmd := &cobra.Command{
		Use:     "abra [cmd] [args] [flags]",
		Short:   "The Co-op Cloud command-line utility belt 🎩🐇",
		Version: fmt.Sprintf("%s-%s", version, commit[:7]),
		ValidArgs: []string{
			"app",
			"autocomplete",
			"catalogue",
			"man",
			"recipe",
			"server",
			"upgrade",
		},
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			paths := []string{
				config.ABRA_DIR,
				config.SERVERS_DIR,
				config.RECIPES_DIR,
				config.LOGS_DIR,
				config.VENDOR_DIR, // TODO(d1): remove > 0.9.x
				config.BACKUP_DIR, // TODO(d1): remove > 0.9.x
			}

			for _, path := range paths {
				if err := os.Mkdir(path, 0764); err != nil {
					if !os.IsExist(err) {
						log.Fatal(err)
					}
					continue
				}
			}

			log.Logger.SetStyles(charmLog.DefaultStyles())
			charmLog.SetDefault(log.Logger)

			if internal.MachineReadable {
				log.SetOutput(os.Stderr)
			}

			if internal.Debug {
				log.SetLevel(log.DebugLevel)
				log.SetOutput(os.Stderr)
				log.SetReportCaller(true)
			}

			log.Debugf("abra version %s, commit %s", version, commit)
		},
	}

	rootCmd.CompletionOptions.DisableDefaultCmd = true

	manCommand := &cobra.Command{
		Use:     "man [flags]",
		Aliases: []string{"m"},
		Short:   "Generate manpage",
		Example: `  # generate the man pages into /usr/local/share/man/man1
  sudo abra man
  sudo mandb

  # read the man pages
  man abra
  man abra-app-deploy`,
		Run: func(cmd *cobra.Command, args []string) {
			header := &doc.GenManHeader{
				Title:   "ABRA",
				Section: "1",
			}

			manDir := "/usr/local/share/man/man1"
			if _, err := os.Stat(manDir); os.IsNotExist(err) {
				log.Fatalf("unable to proceed, '%s' does not exist?")
			}

			err := doc.GenManTree(rootCmd, header, manDir)
			if err != nil {
				log.Fatal(err)
			}

			log.Info("don't forget to run 'sudo mandb'")
		},
	}

	rootCmd.PersistentFlags().BoolVarP(
		&internal.Debug,
		"debug",
		"d",
		false,
		"show debug messages",
	)

	rootCmd.PersistentFlags().BoolVarP(
		&internal.NoInput,
		"no-input",
		"n",
		false,
		"toggle non-interactive mode",
	)

	rootCmd.PersistentFlags().BoolVarP(
		&internal.Offline,
		"offline",
		"o",
		false,
		"prefer offline & filesystem access",
	)

	rootCmd.PersistentFlags().BoolVarP(
		&internal.IgnoreEnvVersion,
		"ignore-env-version",
		"i",
		false,
		"ignore .env version checkout",
	)

	catalogue.CatalogueCommand.AddCommand(
		catalogue.CatalogueGenerateCommand,
	)

	server.ServerCommand.AddCommand(
		server.ServerAddCommand,
		server.ServerListCommand,
		server.ServerPruneCommand,
		server.ServerRemoveCommand,
	)

	recipe.RecipeCommand.AddCommand(
		recipe.RecipeDiffCommand,
		recipe.RecipeFetchCommand,
		recipe.RecipeLintCommand,
		recipe.RecipeListCommand,
		recipe.RecipeNewCommand,
		recipe.RecipeReleaseCommand,
		recipe.RecipeResetCommand,
		recipe.RecipeSyncCommand,
		recipe.RecipeUpgradeCommand,
		recipe.RecipeVersionCommand,
	)

	rootCmd.AddCommand(
		UpgradeCommand,
		AutocompleteCommand,
		manCommand,
		app.AppCommand,
		catalogue.CatalogueCommand,
		server.ServerCommand,
		recipe.RecipeCommand,
	)

	app.AppCmdCommand.AddCommand(
		app.AppCmdListCommand,
	)

	app.AppSecretCommand.AddCommand(
		app.AppSecretGenerateCommand,
		app.AppSecretInsertCommand,
		app.AppSecretRmCommand,
		app.AppSecretLsCommand,
	)

	app.AppVolumeCommand.AddCommand(
		app.AppVolumeListCommand,
		app.AppVolumeRemoveCommand,
	)

	app.AppBackupCommand.AddCommand(
		app.AppBackupListCommand,
		app.AppBackupDownloadCommand,
		app.AppBackupCreateCommand,
		app.AppBackupSnapshotsCommand,
	)

	app.AppCommand.AddCommand(
		app.AppBackupCommand,
		app.AppCheckCommand,
		app.AppCmdCommand,
		app.AppConfigCommand,
		app.AppCpCommand,
		app.AppDeployCommand,
		app.AppListCommand,
		app.AppLogsCommand,
		app.AppNewCommand,
		app.AppPsCommand,
		app.AppRemoveCommand,
		app.AppRestartCommand,
		app.AppRestoreCommand,
		app.AppRollbackCommand,
		app.AppRunCommand,
		app.AppSecretCommand,
		app.AppServicesCommand,
		app.AppUndeployCommand,
		app.AppUpgradeCommand,
		app.AppVolumeCommand,
		app.AppLabelsCommand,
		app.AppEnvCommand,
	)

	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}