package cli

import (
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"os"

	"github.com/spf13/cobra"

	"git.cloudinit.dev/coreci/orca/internal/config"
)

type configCtxKey struct{}

var (
	version   = "0.1.0-dev"
	gitCommit = "unknown"
	buildTime = "unknown"
)

const systemNamespaceRoot = "/root/.orca"

var rootCmd = &cobra.Command{
	Use:   "orca",
	Short: "Orca — offline/CLI-first orchestration engine",
	Long: `Orca is a minimalist, offline-first, CLI-first orchestration engine
inspired by HashiCorp Nomad, prioritizing stability, security, and simplicity
over feature richness.`,
	SilenceUsage:  true,
	SilenceErrors: true,
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		if systemNamespace {
			if existing := os.Getenv("ORCA_HOME"); existing != "" && existing != systemNamespaceRoot {
				return fmt.Errorf("--system conflicts with ORCA_HOME=%q (already set); unset ORCA_HOME or drop --system", existing)
			}
			if err := os.Setenv("ORCA_HOME", systemNamespaceRoot); err != nil {
				return fmt.Errorf("set ORCA_HOME for --system: %w", err)
			}
		}
		if configPath != "" {
			cfg, err := config.Load(configPath)
			if err != nil {
				return fmt.Errorf("load config %s: %w", configPath, err)
			}
			cmd.SetContext(context.WithValue(cmd.Context(), configCtxKey{}, cfg))
		}
		return nil
	},
}

var (
	jsonOutput            bool
	systemNamespace       bool
	configPath            string
	noDeprecationWarnings bool
)

func init() {
	rootCmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "output in JSON format")
	rootCmd.PersistentFlags().BoolVar(&systemNamespace, "system", false, "use system-level namespace root (/root/.orca) instead of user-level (~/.orca)")
	rootCmd.PersistentFlags().StringVar(&configPath, "config", "", "path to config.hcl (overrides ~/.orca/config.hcl)")
	rootCmd.PersistentFlags().BoolVar(&noDeprecationWarnings, "no-deprecation-warnings", false, "suppress v0.9 deprecation warnings (use during `orca upgrade` migrations)")
}

// warnDeprecated emits a v0.9 deprecation warning via slog.Warn unless
// the --no-deprecation-warnings global flag is set. Callers pass a
// human-readable message describing what changed. REQ-068.
func warnDeprecated(msg string) {
	if noDeprecationWarnings {
		return
	}
	slog.Warn(msg)
}

func configFromCtx(ctx context.Context) *config.Config {
	if v, ok := ctx.Value(configCtxKey{}).(*config.Config); ok {
		return v
	}
	return nil
}

func Execute() error {
	return rootCmd.Execute()
}

func printJSON(v any) error {
	enc := json.NewEncoder(rootCmd.OutOrStdout())
	enc.SetIndent("", "  ")
	return enc.Encode(v)
}

func printText(format string, args ...any) {
	fmt.Fprintf(rootCmd.OutOrStdout(), format, args...)
}

func printResult(text string, jsonObj any) {
	if jsonOutput {
		_ = printJSON(jsonObj)
		return
	}
	printText("%s\n", text)
}
