package recipe

import (
	"context"
	"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/urfave/cli/v3"
)

func sortServiceByName(versions [][]string) func(i, j int) bool {
	return func(i, j int) bool {
		// NOTE(d1): corresponds to the `tableCol` definition below
		if versions[i][1] == "app" {
			return true
		}
		return versions[i][1] < versions[j][1]
	}
}

var recipeVersionCommand = cli.Command{
	Name:      "versions",
	Aliases:   []string{"v"},
	Usage:     "List recipe versions",
	UsageText: "abra recipe version <recipe> [options]",
	Flags: []cli.Flag{
		internal.MachineReadableFlag,
	},
	Before:        internal.SubCommandBefore,
	ShellComplete: autocomplete.RecipeNameComplete,
	HideHelp:      true,
	Action: func(ctx context.Context, cmd *cli.Command) error {
		var warnMessages []string

		recipe := internal.ValidateRecipe(cmd)

		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", "NAME", "TAG")

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

				for service, serviceMeta := range meta {
					rows = append(rows, []string{service, serviceMeta.Image, serviceMeta.Tag})
					allRows = append(allRows, []string{version, service, serviceMeta.Image, serviceMeta.Tag})
				}

				sort.Slice(rows, sortServiceByName(rows))

				table.Rows(rows...)

				if !internal.MachineReadable {
					fmt.Println(table)
					log.Infof("VERSION: %s", version)
					fmt.Println()
					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)
			}
		}

		return nil
	},
}
