package cmd

import (
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"testing"

	"github.com/spf13/cobra"

	"git.secluded.site/keld/internal/restic"
)

// setupCompletionConfig writes a small TOML fixture and points KELD_CONFIG_FILE
// at it, so config.Presets() returns deterministic results.
func setupCompletionConfig(t *testing.T) {
	t.Helper()

	dir := t.TempDir()
	cfg := filepath.Join(dir, "config.toml")
	err := os.WriteFile(cfg, []byte(`
[global]
verbose = true

["home@"]
host = "laptop"

["@cloud"]
repo = "/repos/cloud"

["@nas"]
repo = "/repos/nas"

[archive]
json = true
`), 0o600)
	if err != nil {
		t.Fatalf("writing fixture config: %v", err)
	}
	t.Setenv("KELD_CONFIG_FILE", cfg)
	t.Setenv("HOME", dir)
}

func TestCompletePresetOffersPrefixesAndPresets(t *testing.T) {
	setupCompletionConfig(t)

	completions, directive := completePreset(nil, nil, "")
	if directive != cobra.ShellCompDirectiveNoFileComp {
		t.Fatalf("expected NoFileComp, got %v", directive)
	}

	has := make(map[string]bool)
	for _, c := range completions {
		has[c] = true
	}

	for _, want := range []string{
		"home@",      // prefix
		"archive",    // plain preset
		"home@cloud", // composite preset
		"home@nas",   // composite preset
		"@cloud",     // suffix preset
		"@nas",       // suffix preset
	} {
		if !has[want] {
			t.Errorf("missing expected completion %q in %v", want, completions)
		}
	}
}

func TestCompletePresetSuffixCompletion(t *testing.T) {
	setupCompletionConfig(t)

	completions, directive := completePreset(nil, nil, "home@")
	if directive != cobra.ShellCompDirectiveNoFileComp {
		t.Fatalf("expected NoFileComp, got %v", directive)
	}

	sort.Strings(completions)
	want := []string{"home@cloud", "home@nas"}
	if !reflect.DeepEqual(completions, want) {
		t.Fatalf("suffix completions mismatch: got %v, want %v", completions, want)
	}
}

func TestCompletePresetPartialSuffixCompletion(t *testing.T) {
	setupCompletionConfig(t)

	completions, directive := completePreset(nil, nil, "home@c")
	if directive != cobra.ShellCompDirectiveNoFileComp {
		t.Fatalf("expected NoFileComp, got %v", directive)
	}

	want := []string{"home@cloud"}
	if !reflect.DeepEqual(completions, want) {
		t.Fatalf("partial suffix completions mismatch: got %v, want %v", completions, want)
	}
}

func TestCompletePresetPrefixFilter(t *testing.T) {
	setupCompletionConfig(t)

	completions, directive := completePreset(nil, nil, "arc")
	if directive != cobra.ShellCompDirectiveNoFileComp {
		t.Fatalf("expected NoFileComp, got %v", directive)
	}

	want := []string{"archive"}
	if !reflect.DeepEqual(completions, want) {
		t.Fatalf("prefix-filtered completions mismatch: got %v, want %v", completions, want)
	}
}

func TestSubcommandsRegisteredFromResticMetadata(t *testing.T) {
	t.Parallel()

	subcommands := rootCmd.Commands()
	if got, want := len(subcommands), len(restic.Commands)-1; got != want {
		t.Fatalf("subcommand count mismatch: got %d, want %d", got, want)
	}

	seen := make(map[string]*cobra.Command, len(subcommands))
	for _, subcmd := range subcommands {
		seen[subcmd.Name()] = subcmd
	}

	if _, ok := seen["global"]; ok {
		t.Fatal("global command should not be registered as a subcommand")
	}

	for name := range restic.Commands {
		name := name
		if name == "global" {
			continue
		}

		subcmd, ok := seen[name]
		if !ok {
			t.Fatalf("missing subcommand %q", name)
		}
		if !subcmd.DisableFlagParsing {
			t.Fatalf("subcommand %q should have DisableFlagParsing enabled", name)
		}
	}
}

func TestRegisteredRootAndSubcommandFlags(t *testing.T) {
	t.Parallel()

	for _, flagName := range []string{"preset", "show-command", "config", "repo", "verbose"} {
		if rootCmd.PersistentFlags().Lookup(flagName) == nil {
			t.Fatalf("missing expected root persistent flag %q", flagName)
		}
	}

	if rootCmd.PersistentFlags().Lookup("dry-run") != nil {
		t.Fatal("root should not expose a --dry-run flag")
	}

	if got := rootCmd.PersistentFlags().Lookup("verbose").Value.Type(); got != "count" {
		t.Fatalf("expected --verbose to be a count flag, got type %q", got)
	}

	backup := rootCmd.Commands()[0]
	for _, subcmd := range rootCmd.Commands() {
		if subcmd.Name() == "backup" {
			backup = subcmd
			break
		}
	}

	for _, flagName := range []string{"exclude", "dry-run"} {
		if backup.Flags().Lookup(flagName) == nil {
			t.Fatalf("backup subcommand missing expected command flag %q", flagName)
		}
	}

	if backup.InheritedFlags().Lookup("repo") == nil {
		t.Fatal("backup subcommand missing inherited global --repo flag")
	}
}

func TestPresetFlagCompletionRegistered(t *testing.T) {
	setupCompletionConfig(t)

	completionFn, ok := rootCmd.GetFlagCompletionFunc("preset")
	if !ok {
		t.Fatal("root --preset completion function is not registered")
	}

	completions, directive := completionFn(rootCmd, nil, "home@")
	if directive != cobra.ShellCompDirectiveNoFileComp {
		t.Fatalf("expected NoFileComp, got %v", directive)
	}

	sort.Strings(completions)
	want := []string{"home@cloud", "home@nas"}
	if !reflect.DeepEqual(completions, want) {
		t.Fatalf("completion function returned %v, want %v", completions, want)
	}
}
