package cmd

import (
	"bytes"
	"sort"
	"strings"
	"testing"

	"github.com/tidbcloud/clinic-cli/internal/config"
)

func TestCompletionCommandGeneratesBashScript(t *testing.T) {
	setTestConfigHome(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"completion", "bash"}, &stdout); err != nil {
		t.Fatalf("RunWithIO(completion bash) error = %v", err)
	}

	output := stdout.String()
	if !strings.Contains(output, "__start_clinic") {
		t.Fatalf("completion bash output missing __start_clinic helper\noutput:\n%s", output)
	}
	if !strings.Contains(output, "complete -o default -F __start_clinic clinic") {
		t.Fatalf("completion bash output missing clinic registration\noutput:\n%s", output)
	}
}

func TestShellCompletionSuggestsGlobalEnvValues(t *testing.T) {
	setTestConfigHome(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"__complete", "--env", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete --env) error = %v", err)
	}

	assertCompletionItems(t, parseCompletionItems(stdout.String()), []string{"prod", "staging", "dev"})

	stdout.Reset()
	if err := RunWithIO([]string{"__complete", "auth", "--env", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete auth --env) error = %v", err)
	}

	assertCompletionItems(t, parseCompletionItems(stdout.String()), []string{"prod", "staging", "dev"})
}

func TestShellCompletionSuggestsProfilesForSelectedEnv(t *testing.T) {
	setTestConfigHome(t)
	configureCompletionProfilesFixture(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"__complete", "--env", "staging", "--profile", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete --env staging --profile) error = %v", err)
	}

	got := parseCompletionItems(stdout.String())
	assertCompletionItems(t, got, []string{"blue", "green"})
	if containsCompletionItem(got, "red") {
		t.Fatalf("profile completion unexpectedly contained profile from another env: %v", got)
	}
}

func TestShellCompletionSuggestsEnvAndSwitchProfileArguments(t *testing.T) {
	setTestConfigHome(t)
	configureCompletionProfilesFixture(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"__complete", "env", "use", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete env use) error = %v", err)
	}
	assertCompletionItems(t, parseCompletionItems(stdout.String()), []string{"prod", "staging", "dev"})

	stdout.Reset()
	if err := RunWithIO([]string{"__complete", "switch", "staging", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete switch staging) error = %v", err)
	}
	assertCompletionItems(t, parseCompletionItems(stdout.String()), []string{"blue", "green"})
}

func TestShellCompletionSuggestsLokiSubcommands(t *testing.T) {
	setTestConfigHome(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"__complete", "loki", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete loki) error = %v", err)
	}
	assertCompletionItems(t, parseCompletionItems(stdout.String()), []string{"query", "instance-map", "labels", "label-values"})
}

func TestShellCompletionSuggestsDeployTypeValues(t *testing.T) {
	setTestConfigHome(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"__complete", "cluster", "search", "--deploy-type", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete cluster search --deploy-type) error = %v", err)
	}
	assertCompletionItems(t, parseCompletionItems(stdout.String()), knownDeployTypeV2Values)

	stdout.Reset()
	if err := RunWithIO([]string{"__complete", "cluster", "search", "--deploy-type", "byoc"}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete cluster search --deploy-type byoc) error = %v", err)
	}
	got := parseCompletionItems(stdout.String())
	assertCompletionItems(t, got, []string{"byoc", "byoc resource pool"})
	if containsCompletionItem(got, "dedicated") {
		t.Fatalf("deploy-type completion unexpectedly contained non-matching value: %v", got)
	}
}

func TestShellCompletionSuggestsProfileOpenSubcommandAndProfileArguments(t *testing.T) {
	setTestConfigHome(t)
	configureCompletionProfilesFixture(t)

	var stdout bytes.Buffer
	if err := RunWithIO([]string{"__complete", "profile", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete profile) error = %v", err)
	}
	assertCompletionItems(t, parseCompletionItems(stdout.String()), []string{"open"})

	stdout.Reset()
	if err := RunWithIO([]string{"__complete", "--env", "staging", "profile", "open", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete profile open) error = %v", err)
	}
	got := parseCompletionItems(stdout.String())
	assertCompletionItems(t, got, []string{"blue", "green"})
	if containsCompletionItem(got, "red") {
		t.Fatalf("profile open completion unexpectedly contained profile from another env: %v", got)
	}

	for _, subcommand := range []string{"use", "delete"} {
		stdout.Reset()
		if err := RunWithIO([]string{"__complete", "--env", "staging", "profile", subcommand, ""}, &stdout); err != nil {
			t.Fatalf("RunWithIO(__complete profile %s) error = %v", subcommand, err)
		}
		got := parseCompletionItems(stdout.String())
		assertCompletionItems(t, got, []string{"blue", "green"})
		if containsCompletionItem(got, "current") {
			t.Fatalf("profile %s completion contained reserved current alias: %v", subcommand, got)
		}
		if containsCompletionItem(got, "red") {
			t.Fatalf("profile %s completion unexpectedly contained profile from another env: %v", subcommand, got)
		}
	}

	stdout.Reset()
	if err := RunWithIO([]string{"__complete", "--env", "staging", "profile", "set", ""}, &stdout); err != nil {
		t.Fatalf("RunWithIO(__complete profile set) error = %v", err)
	}
	got = parseCompletionItems(stdout.String())
	assertCompletionItems(t, got, []string{"blue", "green", "current"})
	if containsCompletionItem(got, "red") {
		t.Fatalf("profile set completion unexpectedly contained profile from another env: %v", got)
	}
}

func configureCompletionProfilesFixture(t *testing.T) {
	t.Helper()

	cfg := mustLoadConfig(t)
	cfg.EnvProfiles["staging"] = config.EnvProfileContainer{
		ActiveProfile: "blue",
		Profiles: map[string]config.ProfileRecord{
			"blue": {
				ClusterID: "staging-blue",
			},
			"green": {
				ClusterID: "staging-green",
			},
		},
	}
	cfg.EnvProfiles["prod"] = config.EnvProfileContainer{
		ActiveProfile: "red",
		Profiles: map[string]config.ProfileRecord{
			"red": {
				ClusterID: "prod-red",
			},
		},
	}

	if err := cfg.Save(); err != nil {
		t.Fatalf("cfg.Save() error = %v", err)
	}
}

func parseCompletionItems(raw string) []string {
	lines := strings.Split(raw, "\n")
	items := make([]string, 0, len(lines))
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		if strings.HasPrefix(line, ":") {
			continue
		}
		if strings.HasPrefix(line, "[Debug]") {
			continue
		}

		item, _, _ := strings.Cut(line, "\t")
		items = append(items, strings.TrimSpace(item))
	}
	return items
}

func assertCompletionItems(t *testing.T, got []string, want []string) {
	t.Helper()

	for _, item := range want {
		if !containsCompletionItem(got, item) {
			t.Fatalf("completion items = %v, want to contain %q", got, item)
		}
	}

	sorted := append([]string(nil), got...)
	sort.Strings(sorted)
	if len(sorted) == 0 {
		t.Fatalf("completion returned no items")
	}
}

func containsCompletionItem(items []string, target string) bool {
	for _, item := range items {
		if item == target {
			return true
		}
	}
	return false
}
