package recipe

import (
	"fmt"
	"sort"

	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/pkg/autocomplete"
	"coopcloud.tech/abra/pkg/formatter"
	"coopcloud.tech/abra/pkg/log"
	recipePkg "coopcloud.tech/abra/pkg/recipe"
	"github.com/spf13/cobra"
)

var RecipeVersionCommand = &cobra.Command{
	Use:     "versions <recipe> [flags]",
	Aliases: []string{"v"},
	Short:   "List recipe versions",
	Args:    cobra.ExactArgs(1),
	ValidArgsFunction: func(
		cmd *cobra.Command,
		args []string,
		toComplete string) ([]string, cobra.ShellCompDirective) {
		return autocomplete.RecipeNameComplete()
	},
	Run: func(cmd *cobra.Command, args []string) {
		var warnMessages []string

		recipe := internal.ValidateRecipe(args, cmd.Name())

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

		recipeMeta, ok := catl[recipe.Name]
		if !ok {
			warnMessages = append(warnMessages, "retrieved versions from local recipe repository")

			recipeVersions, err := recipe.GetRecipeVersions()
			if err != nil {
				warnMessages = append(warnMessages, err.Error())
			}

			recipeMeta = recipePkg.RecipeMeta{Versions: recipeVersions}
		}

		if len(recipeMeta.Versions) == 0 {
			log.Fatalf("%s has no published versions?", recipe.Name)
		}

		for i := len(recipeMeta.Versions) - 1; i >= 0; i-- {
			table, err := formatter.CreateTable()
			if err != nil {
				log.Fatal(err)
			}

			table.Headers("SERVICE", "IMAGE", "TAG", "VERSION")

			for version, meta := range recipeMeta.Versions[i] {
				var allRows [][]string
				var rows [][]string

				for service, serviceMeta := range meta {
					recipeVersion := version
					if service != "app" {
						recipeVersion = ""
					}

					rows = append(rows, []string{
						service,
						serviceMeta.Image,
						serviceMeta.Tag,
						recipeVersion,
					})

					allRows = append(allRows, []string{
						version,
						service,
						serviceMeta.Image,
						serviceMeta.Tag,
						recipeVersion,
					})
				}

				sort.Slice(rows, sortServiceByName(rows))

				table.Rows(rows...)

				if !internal.MachineReadable {
					if err := formatter.PrintTable(table); err != nil {
						log.Fatal(err)
					}
					continue
				}

				if internal.MachineReadable {
					sort.Slice(allRows, sortServiceByName(allRows))
					headers := []string{"VERSION", "SERVICE", "NAME", "TAG"}
					out, err := formatter.ToJSON(headers, allRows)
					if err != nil {
						log.Fatal("unable to render to JSON: %s", err)
					}
					fmt.Println(out)
					continue
				}
			}
		}

		if !internal.MachineReadable {
			for _, warnMsg := range warnMessages {
				log.Warn(warnMsg)
			}
		}
	},
}

func sortServiceByName(versions [][]string) func(i, j int) bool {
	return func(i, j int) bool {
		return versions[i][0] < versions[j][0]
	}
}

func init() {
	RecipeVersionCommand.Flags().BoolVarP(
		&internal.MachineReadable,
		"machine",
		"m",
		false,
		"print machine-readable output",
	)
}
