package config

import (
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
)

func TestResolve(t *testing.T) {
	tmpDir := t.TempDir()
	homeDir := filepath.Join(tmpDir, "home")
	if err := os.MkdirAll(homeDir, 0o755); err != nil {
		t.Fatalf("creating temp HOME: %v", err)
	}

	configPath := filepath.Join(tmpDir, "config.toml")
	if err := os.WriteFile(configPath, []byte(resolveFixtureTOML), 0o600); err != nil {
		t.Fatalf("writing fixture config: %v", err)
	}

	t.Setenv("HOME", homeDir)
	t.Setenv("KELD_CONFIG_FILE", configPath)
	t.Setenv("KELD_CONFIG_PATHS", "")

	tests := []struct {
		name         string
		preset       string
		command      string
		overrides    map[string][]string
		wantCommand  string
		wantWorkdir  string
		wantArgs     []string
		wantSections []string
		wantEnviron  map[string]string
		wantFlags    map[string][]string
	}{
		{
			name:         "global command only",
			preset:       "",
			command:      "backup",
			wantCommand:  "backup",
			wantWorkdir:  "",
			wantArgs:     nil,
			wantSections: []string{"global", "global.backup"},
			wantEnviron: map[string]string{
				"RESTIC_REPOSITORY":       "/repos/global",
				"RESTIC_PASSWORD_COMMAND": "pass global",
			},
			wantFlags: map[string][]string{
				"--password-file":      {"/secrets/global"},
				"--verbose":            {""},
				"--tag":                {"global-one", "global-two"},
				"--exclude-file":       {"/etc/restic/global-excludes"},
				"--exclude-if-present": {".nobackup"},
				"--cache-dir":          {"/srv/cache"},
			},
		},
		{
			name:         "preset command merge with special keys",
			preset:       "home",
			command:      "backup",
			wantCommand:  "backup",
			wantWorkdir:  filepath.Join(homeDir, "work", "home"),
			wantArgs:     []string{"/home/alice", "/home/shared"},
			wantSections: []string{"global", "global.backup", "home", "home.backup"},
			wantEnviron: map[string]string{
				"RESTIC_REPOSITORY":       "/repos/global",
				"RESTIC_PASSWORD_COMMAND": "pass home",
			},
			wantFlags: map[string][]string{
				"--password-file":      {"/secrets/home"},
				"--verbose":            {""},
				"--tag":                {"home-tag"},
				"--exclude-file":       {"/etc/restic/global-excludes"},
				"--exclude-if-present": {".nobackup"},
				"--cache-dir":          {"/srv/cache"},
				"--repo":               {"/repos/home"},
			},
		},
		{
			name:         "split preset sections",
			preset:       "home@cloud",
			command:      "backup",
			wantCommand:  "backup",
			wantWorkdir:  "",
			wantArgs:     []string{"/data/cloud"},
			wantSections: []string{"global", "global.backup", "@cloud", "home@", "home@.backup", "home@cloud", "home@cloud.backup"},
			wantEnviron: map[string]string{
				"RESTIC_REPOSITORY":       "/repos/cloud",
				"RESTIC_PASSWORD_COMMAND": "pass cloud backup",
			},
			wantFlags: map[string][]string{
				"--password-file":      {"/secrets/global"},
				"--verbose":            {""},
				"--tag":                {"cloud-tag"},
				"--exclude-file":       {"/etc/restic/home-split-excludes"},
				"--exclude-if-present": {".nobackup"},
				"--cache-dir":          {"/srv/cache"},
				"--repo":               {"/repos/home-cloud"},
			},
		},
		{
			name:         "command alias",
			preset:       "archive",
			command:      "backup",
			wantCommand:  "snapshots",
			wantWorkdir:  "",
			wantArgs:     []string{"latest"},
			wantSections: []string{"global", "global.backup", "archive", "archive.backup"},
			wantEnviron: map[string]string{
				"RESTIC_REPOSITORY":       "/repos/global",
				"RESTIC_PASSWORD_COMMAND": "pass global",
			},
			wantFlags: map[string][]string{
				"--password-file":      {"/secrets/global"},
				"--verbose":            {""},
				"--tag":                {"global-one", "global-two"},
				"--exclude-file":       {"/etc/restic/global-excludes"},
				"--exclude-if-present": {".nobackup"},
				"--cache-dir":          {"/srv/cache"},
				"--json":               {""},
			},
		},
		{
			name:    "cli overrides take precedence",
			preset:  "home",
			command: "backup",
			overrides: map[string][]string{
				"tag":                {"cli-a", "cli-b"},
				"password-file":      {"/secrets/cli"},
				"repo":               {"/repos/cli"},
				"json":               nil,
				"_arguments":         {"/cli/path"},
				"_workdir":           {"~/work/cli"},
				"_command":           {"backup"},
				"exclude-if-present": {".override-marker"},
			},
			wantCommand:  "backup",
			wantWorkdir:  filepath.Join(homeDir, "work", "cli"),
			wantArgs:     []string{"/cli/path"},
			wantSections: []string{"global", "global.backup", "home", "home.backup"},
			wantEnviron: map[string]string{
				"RESTIC_REPOSITORY":       "/repos/global",
				"RESTIC_PASSWORD_COMMAND": "pass home",
			},
			wantFlags: map[string][]string{
				"--password-file":      {"/secrets/cli"},
				"--verbose":            {""},
				"--tag":                {"cli-a", "cli-b"},
				"--exclude-file":       {"/etc/restic/global-excludes"},
				"--exclude-if-present": {".override-marker"},
				"--cache-dir":          {"/srv/cache"},
				"--repo":               {"/repos/cli"},
				"--json":               {""},
			},
		},
	}

	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			cfg, err := Resolve(tt.preset, tt.command, tt.overrides)
			if err != nil {
				t.Fatalf("Resolve() error: %v", err)
			}

			if cfg.Command != tt.wantCommand {
				t.Fatalf("command mismatch: got %q, want %q", cfg.Command, tt.wantCommand)
			}
			if cfg.Workdir != tt.wantWorkdir {
				t.Fatalf("workdir mismatch: got %q, want %q", cfg.Workdir, tt.wantWorkdir)
			}
			if !equalStrings(cfg.Arguments, tt.wantArgs) {
				t.Fatalf("arguments mismatch: got %#v, want %#v", cfg.Arguments, tt.wantArgs)
			}
			if !reflect.DeepEqual(cfg.SectionsRead, tt.wantSections) {
				t.Fatalf("sections mismatch: got %#v, want %#v", cfg.SectionsRead, tt.wantSections)
			}
			if !reflect.DeepEqual(cfg.Environ, tt.wantEnviron) {
				t.Fatalf("environ mismatch: got %#v, want %#v", cfg.Environ, tt.wantEnviron)
			}

			for _, f := range cfg.Flags {
				if !strings.HasPrefix(f.Name, "-") {
					t.Fatalf("flag name %q is missing CLI prefix", f.Name)
				}
			}

			gotFlags := collectFlags(cfg.Flags)
			if !reflect.DeepEqual(gotFlags, tt.wantFlags) {
				t.Fatalf("flags mismatch: got %#v, want %#v", gotFlags, tt.wantFlags)
			}
		})
	}
}

func collectFlags(flags []Flag) map[string][]string {
	out := make(map[string][]string)
	for _, flag := range flags {
		out[flag.Name] = append(out[flag.Name], flag.Value)
	}
	return out
}

func equalStrings(a, b []string) bool {
	if len(a) == 0 && len(b) == 0 {
		return true
	}
	return reflect.DeepEqual(a, b)
}

const resolveFixtureTOML = `
[vars]
cache-root = "/srv"

[global]
password-file = "/secrets/global"
verbose = true
tag = ["global-one", "global-two"]

[global.backup]
exclude-file = "/etc/restic/global-excludes"
exclude-if-present = ".nobackup"
cache-dir = "${vars.cache-root}/cache"

[global.backup.environ]
RESTIC_REPOSITORY = "/repos/global"
RESTIC_PASSWORD_COMMAND = "pass global"

[home]
repo = "/repos/home"
_workdir = "~/work/home"
tag = ["home-tag"]

[home.backup]
_arguments = ["/home/alice", "/home/shared"]
password-file = "/secrets/home"

[home.backup.environ]
RESTIC_PASSWORD_COMMAND = "pass home"

["@cloud"]
repo = "/repos/cloud-base"
tag = ["cloud-tag"]

["@cloud".environ]
RESTIC_REPOSITORY = "/repos/cloud"

["home@"]
repo = "/repos/home-prefix"

["home@".backup]
exclude-file = "/etc/restic/home-split-excludes"

["home@".forget]
keep-last = 7

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

["home@cloud".backup]
_arguments = ["/data/cloud"]

["home@cloud".backup.environ]
RESTIC_PASSWORD_COMMAND = "pass cloud backup"

[archive.backup]
_command = "snapshots"
_arguments = ["latest"]
json = true
`
