package cmd

import (
	"fmt"

	"github.com/kubeasy-dev/kubeasy-cli/internal/api"
	"github.com/kubeasy-dev/kubeasy-cli/internal/audit"
	"github.com/kubeasy-dev/kubeasy-cli/internal/constants"
	"github.com/kubeasy-dev/kubeasy-cli/internal/deployer"
	"github.com/kubeasy-dev/kubeasy-cli/internal/kube"
	"github.com/kubeasy-dev/kubeasy-cli/internal/logger"
	"github.com/kubeasy-dev/kubeasy-cli/internal/semver"
	"github.com/kubeasy-dev/kubeasy-cli/internal/ui"
	"github.com/kubeasy-dev/kubeasy-cli/internal/validation"
	"github.com/spf13/cobra"
)

var (
	apiGetChallenge         = api.GetChallengeBySlug
	apiGetChallengeProgress = api.GetChallengeStatus
	apiStartChallenge       = api.StartChallengeWithResponse
)

var startChallengeCmd = &cobra.Command{
	Use:   "start [challenge-slug]",
	Short: "Start a challenge",
	Long:  `Starts a challenge by installing the necessary components into the local Kubernetes cluster.`,
	Args:  cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		challengeSlug := args[0]

		// SAFE-02: validate slug before any API or cluster call
		if err := validateChallengeSlug(challengeSlug); err != nil {
			return err
		}

		ui.Section(fmt.Sprintf("Starting Challenge: %s", challengeSlug))

		// Fetch challenge details
		var challenge *api.ChallengeEntity
		err := ui.WaitMessage("Fetching challenge details", func() error {
			var err error
			challenge, err = apiGetChallenge(cmd.Context(), challengeSlug)
			return err
		})
		if err != nil {
			ui.Error("Failed to fetch challenge")
			return fmt.Errorf("failed to fetch challenge: %w", err)
		}

		ui.Info(fmt.Sprintf("Challenge: %s", challenge.Title))

		// Check progress
		var progress *api.ChallengeStatusResponse
		err = ui.WaitMessage("Checking challenge progress", func() error {
			var err error
			progress, err = apiGetChallengeProgress(cmd.Context(), challengeSlug)
			return err
		})
		if err != nil {
			ui.Error("Failed to fetch challenge progress")
			return fmt.Errorf("failed to fetch challenge progress: %w", err)
		}

		if progress != nil && (progress.Status == "in_progress" || progress.Status == "completed") {
			ui.Warning("Challenge already started")
			ui.Info(fmt.Sprintf("Continue the challenge or reset it with 'kubeasy challenge reset %s'", challengeSlug))
			return nil // Not an error, just already started
		}

		// Check minimum required CLI version
		if err := checkMinRequiredVersion(challengeSlug); err != nil {
			return err
		}

		// Setup environment - use context from command
		ctx := cmd.Context()
		ui.Println()

		// Step 1: Create namespace
		dynamicClient, err := kube.GetDynamicClient()
		if err != nil {
			ui.Error("Failed to get Kubernetes dynamic client")
			return fmt.Errorf("failed to get dynamic client: %w", err)
		}

		staticClient, err := kube.GetKubernetesClient()
		if err != nil {
			ui.Error("Failed to get Kubernetes static client")
			return fmt.Errorf("failed to get static client: %w", err)
		}

		err = ui.WaitMessage("Creating namespace", func() error {
			return kube.CreateNamespace(ctx, staticClient, challengeSlug)
		})
		if err != nil {
			ui.Error("Failed to create namespace")
			return fmt.Errorf("failed to create namespace: %w", err)
		}

		// Step 2: Deploy challenge via API proxy
		err = ui.WaitMessage("Deploying challenge", func() error {
			_, err := deployer.DeployChallengeFromRegistry(ctx, staticClient, dynamicClient, challengeSlug)
			return err
		})
		if err != nil {
			ui.Error("Failed to deploy challenge")
			return fmt.Errorf("failed to deploy challenge: %w", err)
		}

		// Step 3: Configure context
		if err := kube.SetNamespaceForContext(constants.KubeasyClusterContext, challengeSlug); err != nil {
			logger.Debug("Failed to set namespace for context: %v", err)
			ui.Warning("Could not configure kubectl context namespace")
		} else {
			ui.Success("Kubectl context configured")
		}

		// Step 4: Register progress
		err = ui.WaitMessage("Registering challenge progress", func() error {
			_, err = apiStartChallenge(cmd.Context(), challengeSlug)
			return err
		})
		if err != nil {
			ui.Error("Failed to start challenge")
			return fmt.Errorf("failed to start challenge: %w", err)
		}

		if err := audit.SaveTimestamp(challengeSlug); err != nil {
			logger.Debug("Could not save start timestamp: %v", err)
		}

		ui.Println()
		ui.Success("Challenge environment is ready!")
		ui.KeyValue("Challenge", challengeSlug)
		ui.KeyValue("Namespace", challengeSlug)
		ui.KeyValue("Context", "kind-kubeasy")
		ui.Println()
		ui.Info("You can now start working on the challenge!")
		return nil
	},
}

// checkMinRequiredVersion loads challenge.yaml for the given slug and verifies
// the running CLI version meets the minRequiredVersion constraint.
// It is a no-op when the field is absent or the CLI is a pre-release build.
func checkMinRequiredVersion(slug string) error {
	spec, err := validation.LoadChallengeYamlForChallenge(slug)
	if err != nil {
		// Non-fatal: if challenge.yaml is unavailable we cannot block the user.
		logger.Debug("Could not load challenge.yaml for version check: %v", err)
		return nil
	}

	required := spec.MinRequiredVersion
	if required == "" {
		return nil
	}

	current := constants.Version
	if semver.IsPreRelease(current) {
		logger.Debug("Pre-release CLI build (%s), skipping minRequiredVersion check", current)
		return nil
	}

	curNorm := semver.Normalize(current)
	reqNorm := semver.Normalize(required)
	if semver.Compare(curNorm, reqNorm) < 0 {
		return fmt.Errorf(
			"this challenge requires kubeasy-cli >= %s (you have %s)\nPlease update: %s/kubeasy-cli/releases/latest",
			required, current, constants.GithubRootURL,
		)
	}

	return nil
}

func init() {
	challengeCmd.AddCommand(startChallengeCmd)
}
