package paths

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

func TestRoot_HonorsORCAHOME(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	if got, want := Root(), dir; got != want {
		t.Errorf("Root() = %q, want %q", got, want)
	}
}

func TestRoot_EmptyORCAHOMEFallsBack(t *testing.T) {
	t.Setenv("ORCA_HOME", "")
	home, err := os.UserHomeDir()
	if err != nil {
		t.Skipf("os.UserHomeDir: %v", err)
	}
	want := filepath.Join(home, defaultHomeSubdir)
	if got := Root(); got != want {
		t.Errorf("Root() with empty ORCA_HOME = %q, want %q", got, want)
	}
}

func TestRoot_UnsetORCAHOMEFallsBack(t *testing.T) {
	os.Unsetenv("ORCA_HOME")
	home, err := os.UserHomeDir()
	if err != nil {
		t.Skipf("os.UserHomeDir: %v", err)
	}
	want := filepath.Join(home, defaultHomeSubdir)
	got := Root()
	if got != want {
		t.Errorf("Root() default = %q, want %q", got, want)
	}
	if !strings.HasPrefix(got, home) {
		t.Errorf("Root() default %q does not start with home %q", got, home)
	}
}

func TestRoot_RelativeORCAHOME(t *testing.T) {
	t.Setenv("ORCA_HOME", "relative/orca/home")
	if got, want := Root(), "relative/orca/home"; got != want {
		t.Errorf("Root() relative = %q, want %q", got, want)
	}
}

func TestDefaultNamespace(t *testing.T) {
	if got, want := DefaultNamespace(), "_defaults"; got != want {
		t.Errorf("DefaultNamespace() = %q, want %q", got, want)
	}
}

func TestClusterDir(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	got := ClusterDir()
	want := filepath.Join(dir, "cluster")
	if got != want {
		t.Errorf("ClusterDir() = %q, want %q", got, want)
	}
	if !strings.HasPrefix(got, Root()+string(filepath.Separator)) {
		t.Errorf("ClusterDir() %q not under Root() %q", got, Root())
	}
}

func TestNamespaceDir(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	ns := "prod"
	got := NamespaceDir(ns)
	want := filepath.Join(dir, ns)
	if got != want {
		t.Errorf("NamespaceDir(%q) = %q, want %q", ns, got, want)
	}
	if !strings.HasPrefix(got, Root()+string(filepath.Separator)) {
		t.Errorf("NamespaceDir() %q not under Root() %q", got, Root())
	}
}

func TestNamespacePaths(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	ns := "prod"
	nsDir := NamespaceDir(ns)

	cases := []struct {
		name string
		got  string
		want string
	}{
		{"NSDb", NSDb(ns), filepath.Join(nsDir, "db", "orca.db")},
		{"NSEnv", NSEnv(ns), filepath.Join(nsDir, ".env")},
		{"NSSecrets", NSSecrets(ns), filepath.Join(nsDir, ".env.secrets")},
		{"NSJobs", NSJobs(ns), filepath.Join(nsDir, "jobs")},
		{"NSAlloc", NSAlloc(ns), filepath.Join(nsDir, "alloc")},
		{"NSMd", NSMd(ns), filepath.Join(nsDir, "ns.md")},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.got != tc.want {
				t.Errorf("%s(%q) = %q, want %q", tc.name, ns, tc.got, tc.want)
			}
			if !strings.HasPrefix(tc.got, nsDir+string(filepath.Separator)) {
				t.Errorf("%s() %q not under NamespaceDir() %q", tc.name, tc.got, nsDir)
			}
		})
	}
}

func TestDefaultNamespacePaths(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	ns := DefaultNamespace()
	nsDir := NamespaceDir(ns)

	if got, want := NSDb(ns), filepath.Join(nsDir, "db", "orca.db"); got != want {
		t.Errorf("NSDb(_defaults) = %q, want %q", got, want)
	}
	if got, want := NSEnv(ns), filepath.Join(nsDir, ".env"); got != want {
		t.Errorf("NSEnv(_defaults) = %q, want %q", got, want)
	}
}

func TestClusterPaths(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	cDir := ClusterDir()

	cases := []struct {
		name string
		got  string
		want string
	}{
		{"CACertPath", CACertPath(), filepath.Join(cDir, "ca.crt")},
		{"CAKeyPath", CAKeyPath(), filepath.Join(cDir, "ca.key")},
		{"MasterKeyPath", MasterKeyPath(), filepath.Join(cDir, "master.key")},
		{"KnownHostsPath", KnownHostsPath(), filepath.Join(cDir, "known_hosts")},
		{"SSHKeyPath", SSHKeyPath(), filepath.Join(cDir, "orca_ssh_key")},
		{"SSHPubPath", SSHPubPath(), filepath.Join(cDir, "orca_ssh_key.pub")},
		{"ServerCertPath", ServerCertPath(), filepath.Join(cDir, "server.crt")},
		{"ServerKeyPath", ServerKeyPath(), filepath.Join(cDir, "server.key")},
		{"ConfigPath", ConfigPath(), filepath.Join(cDir, "config.md")},
		{"LegacyHCLConfigPath", LegacyHCLConfigPath(), filepath.Join(cDir, "config.hcl")},
		{"TxnDir", TxnDir(), filepath.Join(cDir, "txns")},
		{"PeersDir", PeersDir(), filepath.Join(cDir, "peers")},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.got != tc.want {
				t.Errorf("%s() = %q, want %q", tc.name, tc.got, tc.want)
			}
			if !strings.HasPrefix(tc.got, cDir+string(filepath.Separator)) {
				t.Errorf("%s() %q not under ClusterDir() %q", tc.name, tc.got, cDir)
			}
		})
	}
}

func TestPeerDir(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	host := "node1.example.com"
	got := PeerDir(host)
	want := filepath.Join(PeersDir(), host)
	if got != want {
		t.Errorf("PeerDir(%q) = %q, want %q", host, got, want)
	}
	if !strings.HasPrefix(got, PeersDir()+string(filepath.Separator)) {
		t.Errorf("PeerDir() %q not under PeersDir() %q", got, PeersDir())
	}
}

func TestCacheDB(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	got := CacheDB()
	want := filepath.Join(dir, "orca_cache.db")
	if got != want {
		t.Errorf("CacheDB() = %q, want %q", got, want)
	}
	if !strings.HasPrefix(got, Root()+string(filepath.Separator)) {
		t.Errorf("CacheDB() %q not under Root() %q", got, Root())
	}
}

func TestPathSeparatorsOSAppropriate(t *testing.T) {
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	// Every returned path must use the OS separator (filepath.Join).
	sep := string(filepath.Separator)
	for _, p := range []string{
		ClusterDir(), NamespaceDir("ns"), NSDb("ns"), NSEnv("ns"),
		NSSecrets("ns"), NSJobs("ns"), NSAlloc("ns"), NSMd("ns"),
		CACertPath(), CAKeyPath(), MasterKeyPath(), CacheDB(),
		TxnDir(), PeersDir(), PeerDir("h"), KnownHostsPath(),
		SSHKeyPath(), SSHPubPath(), ServerCertPath(), ServerKeyPath(),
		ConfigPath(), LegacyHCLConfigPath(),
	} {
		if !strings.Contains(p, sep) {
			t.Errorf("path %q lacks OS separator %q (not joined?)", p, sep)
		}
	}
}
