package cmd

import (
	"os"

	"github.com/innerr/ticat/pkg/core/model"

	"github.com/tidbcloud/controlplane9/internal/config"
	cp9errors "github.com/tidbcloud/controlplane9/internal/errors"
)

// registerCommands registers all cp9 subcommands on the ticat command tree.
func registerCommands(tree *model.CmdTree) {
	// --- O1: Local Ephemeral Dev Environment ---
	tree.AddSub("up").
		RegCmd(upCmd(), "create the selected host and deploy services using derived ownership and an AL2023 image", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l").
		AddArg("dry-run", "false").
		AddArg("starter-pre-provision", "false")

	tree.AddSub("destroy", "down").
		RegCmd(destroyCmd(), "tear down k3d cluster and clean up all resources", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l")

	tree.AddSub("deploy", "d").
		RegCmd(deployCmd(), "build and deploy a single service to the local cluster", "").
		AddArg("service", "", "svc", "s").
		AddArg("runtime", "").
		AddArg("dry-run", "false").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l")

	artifactsTree := tree.AddSub("artifacts")
	artifactsTree.AddSub("resolve").
		RegCmd(artifactsResolveCmd(), "resolve and validate an immutable Dev or Staging owner-artifact snapshot", "").
		AddArg("environment", "dev").
		AddArg("output", ".cp9-artifacts").
		AddArg("github-app-client-id", "Iv23liKDo1S8LDZ07CZU").
		AddArg("token-helper", "personas/dev-full/build/remote/github-app-user-token.sh")
	artifactsTree.AddSub("refresh").
		RegCmd(artifactsRefreshCmd(), "refresh selected artifacts or deploy one explicit service image in a Ready session", "").
		AddArg("artifacts", "").
		AddArg("image", "").
		AddArg("runtime", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l")

	tree.AddSub("status", "st").
		RegCmd(statusCmd(), "show environment status and service health", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l").
		AddArg("watch", "false", "w")

	tree.AddSub("browser-acceptance").
		RegCmd(browserAcceptanceCmd(), "record a redacted real Auth0 browser acceptance result", "").
		AddArg("outcome", "").
		AddArg("stage", "").
		AddArg("path", "").
		AddArg("config", config.DefaultConfigFile, "c")

	tree.AddSub("doctor", "doc").
		RegCmd(doctorCmd(), "run read-only preflight for derived ownership, AL2023 image, and session readiness", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l")

	starterTree := tree.AddSub("starter")
	starterTree.AddSub("activate").
		RegCmd(starterActivateCmd(), "activate the imported Starter regional EKS substrate for a Dev full session", "").
		AddArg("config", config.DefaultConfigFile, "c")
	starterTree.AddSub("status", "st").
		RegCmd(starterStatusCmd(), "show imported Starter regional EKS substrate readiness for a Dev full session", "").
		AddArg("config", config.DefaultConfigFile, "c")
	starterTree.AddSub("dashboard").
		RegCmd(starterDashboardCmd(), "open session-owned localhost access to the Starter Grafana dashboard", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local-port", "3000")

	essentialV2Tree := tree.AddSub("essential-v2")
	essentialV2Tree.AddSub("activate").
		RegCmd(essentialV2ActivateCmd(), "activate the Essential V2 shared pool for a Dev full session", "").
		AddArg("config", config.DefaultConfigFile, "c")
	essentialV2Tree.AddSub("status", "st").
		RegCmd(essentialV2StatusCmd(), "show Essential V2 shared-pool readiness for a Dev full session", "").
		AddArg("config", config.DefaultConfigFile, "c")

	byocTree := tree.AddSub("byoc")
	byocTree.AddSub("activate").
		RegCmd(byocActivateCmd(), "activate the BYOC Region for a Dev full remote session", "").
		AddArg("config", config.DefaultConfigFile, "c")
	byocTree.AddSub("status", "st").
		RegCmd(byocStatusCmd(), "show read-only BYOC Region readiness for a Dev full remote session", "").
		AddArg("config", config.DefaultConfigFile, "c")

	tree.AddSub("refresh-creds", "refresh-credentials").
		RegCmd(refreshCredsCmd(), "re-mint AWS session credentials and patch the aws-creds secret in all consuming namespaces", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l").
		AddArg("restart", "false")

	dbTree := tree.AddSub("db")
	dbTree.AddSub("dump-init").
		RegCmd(dbDumpInitCmd(), "dump a MySQL database into a persona init SQL file", "").
		AddArg("url", "", "u").
		AddArg("url-env", "").
		AddArg("database", "", "db").
		AddArg("target-database", "", "target-db").
		AddArg("output", "", "o").
		AddArg("tables", "", "t").
		AddArg("where", "").
		AddArg("where-file", "").
		AddArg("include-deleted", "false")

	tree.AddSub("logs", "log", "l").
		RegCmd(logsCmd(), "view service logs with optional cross-service tracing", "").
		AddArg("service", "", "svc", "s").
		AddArg("trace-id", "", "tid").
		AddArg("since", "", "sn").
		AddArg("level", "", "lvl").
		AddArg("limit", "200", "n").
		AddArg("runtime", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("local", config.DefaultLocalConfigFile, "l")

	tree.AddSub("setup").
		RegCmd(setupCmd(), "use the latest release, then validate AWS access, derived ownership, and the remote AL2023 image plan", "").
		AddArg("config", config.DefaultConfigFile, "c").
		AddArg("checkout", "true")

	// purge has no short alias: the public surface is cleanup intent only.
	// purge ACTS (destructive) on tagged or trusted-parent-provable resources;
	// for standalone orphans it cannot prove, use `cp9 inventory`.
	tree.AddSub("purge").
		RegCmd(purgeCmd(),
			"destructive: clean tagged or trusted-parent-provable cp9 cloud resources (dry-run default). "+
				"For standalone orphaned resources purge cannot prove, use `cp9 inventory`. See docs/adr/0007.", "").
		AddArg("dry-run", "true").
		AddArg("region", "").
		AddArg("owner", "").
		AddArg("session", "").
		AddArg("known-resources", "")

	// inventory is a read-only CP9 attribution namespace; each leaf scans one
	// resource family so the command name stays honest as collectors are added.
	// inventory SEES (never acts) the standalone/unprovable orphaned resources
	// purge will not touch — the complement of purge. See docs/adr/0007.
	inventoryTree := tree.AddSub("inventory")
	inventoryTree.AddSub("volumes").
		RegCmd(inventoryVolumesCmd(),
			"read-only: inventory + classify cp9 EC2 volumes, incl. standalone orphaned ones purge cannot safely clean. "+
				"Never tags or deletes. See docs/adr/0007.", "").
		AddArg("region", "").
		AddArg("summary-only", "false")
	inventoryTree.AddSub("instances").
		RegCmd(inventoryInstancesCmd(),
			"read-only: inventory + classify cp9 EC2 instances. Never tags or deletes. See docs/adr/0007.", "").
		AddArg("region", "").
		AddArg("summary-only", "false")
	inventoryTree.AddSub("security-groups").
		RegCmd(inventorySecurityGroupsCmd(),
			"read-only: inventory + classify cp9 EC2 security groups (default SG protected). "+
				"Never tags or deletes. See docs/adr/0007.", "").
		AddArg("region", "").
		AddArg("summary-only", "false")
	inventoryTree.AddSub("eks").
		RegCmd(inventoryEKSCmd(),
			"read-only: inventory + classify EKS clusters, including missing-scope metadata clusters. "+
				"Never tags or deletes. See docs/adr/0007.", "").
		AddArg("region", "").
		AddArg("summary-only", "false")
}

func explicitLocalPath(args model.ArgVals) (string, bool) {
	localPath := args.GetRaw("local")
	arg, ok := args["local"]
	if !ok || !arg.Provided || arg.Raw == "" {
		return localPath, false
	}
	return localPath, true
}

func rejectV1Alpha2LocalConfigOverride(localPath string, localProvided bool) error {
	if !localProvided {
		return nil
	}
	if _, statErr := os.Stat(localPath); statErr == nil {
		return cp9errors.New(cp9errors.CategoryConfig,
			"cp9.local.yaml is not used with apiVersion cp9.tidbcloud.com/v1alpha2 unified session configs")
	} else if !os.IsNotExist(statErr) {
		return cp9errors.Newf(cp9errors.CategoryConfig,
			"cannot access local config file %q: %v", localPath, statErr)
	}
	return nil
}
