package recipe

import (
	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/pkg/autocomplete"
	"coopcloud.tech/abra/pkg/formatter"
	"coopcloud.tech/abra/pkg/lint"
	"coopcloud.tech/abra/pkg/log"
	"github.com/spf13/cobra"
)

var RecipeLintCommand = &cobra.Command{
	Use:     "lint <recipe> [flags]",
	Short:   "Lint a recipe",
	Aliases: []string{"l"},
	Args:    cobra.MinimumNArgs(1),
	ValidArgsFunction: func(
		cmd *cobra.Command,
		args []string,
		toComplete string) ([]string, cobra.ShellCompDirective) {
		return autocomplete.RecipeNameComplete()
	},
	Run: func(cmd *cobra.Command, args []string) {
		recipe := internal.ValidateRecipe(args, cmd.Name())

		if err := recipe.Ensure(internal.GetEnsureContext()); err != nil {
			log.Fatal(err)
		}

		headers := []string{
			"ref",
			"rule",
			"severity",
			"satisfied",
			"skipped",
			"resolve",
		}

		table, err := formatter.CreateTable()
		if err != nil {
			log.Fatal(err)
		}

		table.Headers(headers...)

		hasError := false
		var rows [][]string
		var warnMessages []string
		for level := range lint.LintRules {
			for _, rule := range lint.LintRules[level] {
				if onlyError && rule.Level != "error" {
					log.Debugf("skipping %s, does not have level \"error\"", rule.Ref)
					continue
				}

				skipped := false
				if rule.Skip(recipe) {
					skipped = true
				}

				skippedOutput := "-"
				if skipped {
					skippedOutput = "✅"
				}

				satisfied := false
				if !skipped {
					ok, err := rule.Function(recipe)
					if err != nil {
						warnMessages = append(warnMessages, err.Error())
					}

					if !ok && rule.Level == "error" {
						hasError = true
					}

					if ok {
						satisfied = true
					}
				}

				satisfiedOutput := "✅"
				if !satisfied {
					satisfiedOutput = "❌"
					if skipped {
						satisfiedOutput = "-"
					}
				}

				row := []string{
					rule.Ref,
					rule.Description,
					rule.Level,
					satisfiedOutput,
					skippedOutput,
					rule.HowToResolve,
				}

				rows = append(rows, row)
				table.Row(row...)
			}
		}

		if len(rows) > 0 {
			if err := formatter.PrintTable(table); err != nil {
				log.Fatal(err)
			}

			for _, warnMsg := range warnMessages {
				log.Warn(warnMsg)
			}

			if hasError {
				log.Warnf("critical errors present in %s config", recipe.Name)
			}
		}
	},
}

var (
	onlyError bool
)

func init() {
	RecipeLintCommand.Flags().BoolVarP(
		&internal.Chaos,
		"chaos",
		"C",
		false,
		"ignore uncommitted recipes changes",
	)

	RecipeLintCommand.Flags().BoolVarP(
		&onlyError,
		"error",
		"e",
		false,
		"only show errors",
	)
}
