package config

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

const exampleHCL = `
db_path          = "/tmp/orca/test.db"
listen_addr      = "127.0.0.1:9999"
ca_path          = "/tmp/orca/ca.crt"
server_cert_path = "/tmp/orca/server.crt"
server_key_path  = "/tmp/orca/server.key"

node_capacity {
  cpu       = 4
  memory_mb = 8192
}
`

func writeFile(t *testing.T, dir, name, content string) string {
	t.Helper()
	p := filepath.Join(dir, name)
	if err := os.WriteFile(p, []byte(content), 0644); err != nil {
		t.Fatalf("write %s: %v", p, err)
	}
	return p
}

func TestLoad_Valid(t *testing.T) {
	p := writeFile(t, t.TempDir(), "config.hcl", exampleHCL)
	cfg, err := Load(p)
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.DBPath != "/tmp/orca/test.db" {
		t.Errorf("DBPath=%q", cfg.DBPath)
	}
	if cfg.ListenAddr != "127.0.0.1:9999" {
		t.Errorf("ListenAddr=%q", cfg.ListenAddr)
	}
	if cfg.CAPath != "/tmp/orca/ca.crt" {
		t.Errorf("CAPath=%q", cfg.CAPath)
	}
	if cfg.ServerCertPath != "/tmp/orca/server.crt" {
		t.Errorf("ServerCertPath=%q", cfg.ServerCertPath)
	}
	if cfg.ServerKeyPath != "/tmp/orca/server.key" {
		t.Errorf("ServerKeyPath=%q", cfg.ServerKeyPath)
	}
	if cfg.NodeCapacity == nil {
		t.Fatal("NodeCapacity nil")
	}
	if cfg.NodeCapacity.CPU != 4 {
		t.Errorf("CPU=%d", cfg.NodeCapacity.CPU)
	}
	if cfg.NodeCapacity.MemoryMB != 8192 {
		t.Errorf("MemoryMB=%d", cfg.NodeCapacity.MemoryMB)
	}
}

func TestLoad_Missing(t *testing.T) {
	cfg, err := Load(filepath.Join(t.TempDir(), "nope.hcl"))
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg == nil {
		t.Fatal("nil config")
	}
	if cfg.DBPath != "" || cfg.ListenAddr != "" || cfg.NodeCapacity != nil {
		t.Errorf("expected zero config, got %+v", cfg)
	}
}

func TestLoad_Malformed(t *testing.T) {
	p := writeFile(t, t.TempDir(), "bad.hcl", "db_path = ")
	cfg, err := Load(p)
	if err == nil {
		t.Fatalf("expected error, got %+v", cfg)
	}
}

func TestLoad_FirstExisting(t *testing.T) {
	dir := t.TempDir()
	existing := writeFile(t, dir, "real.hcl", exampleHCL)
	missing := filepath.Join(dir, "missing.hcl")
	cfg, err := Load(missing, existing)
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.DBPath != "/tmp/orca/test.db" {
		t.Errorf("DBPath=%q", cfg.DBPath)
	}
}

func strPtr(s string) *string { return &s }
func intPtr(i int) *int       { return &i }

func TestMergeOverrides_FlagWins(t *testing.T) {
	cfg := &Config{
		DBPath:     "/file.db",
		ListenAddr: "127.0.0.1:9000",
		NodeCapacity: &CapacityConfig{
			CPU:      4,
			MemoryMB: 8192,
		},
	}
	flags := Flags{
		DBPath:     strPtr("/flag.db"),
		ListenAddr: strPtr("0.0.0.0:1234"),
	}
	env := Environ{"ORCA_DB": "/env.db"}
	out := cfg.MergeOverrides(flags, env)
	if out.DBPath != "/flag.db" {
		t.Errorf("DBPath=%q want /flag.db", out.DBPath)
	}
	if out.ListenAddr != "0.0.0.0:1234" {
		t.Errorf("ListenAddr=%q want 0.0.0.0:1234", out.ListenAddr)
	}
	if cfg.DBPath != "/file.db" {
		t.Errorf("receiver mutated: %q", cfg.DBPath)
	}
}

func TestMergeOverrides_EnvWinsOverFile(t *testing.T) {
	cfg := &Config{DBPath: "/file.db", ListenAddr: "127.0.0.1:9000"}
	env := Environ{"ORCA_DB": "/env.db"}
	out := cfg.MergeOverrides(Flags{}, env)
	if out.DBPath != "/env.db" {
		t.Errorf("DBPath=%q want /env.db", out.DBPath)
	}
	if out.ListenAddr != "127.0.0.1:9000" {
		t.Errorf("ListenAddr=%q want 127.0.0.1:9000", out.ListenAddr)
	}
}

func TestMergeOverrides_FileWinsOverDefault(t *testing.T) {
	cfg := &Config{DBPath: "/file.db", ListenAddr: "127.0.0.1:9000"}
	out := cfg.MergeOverrides(Flags{}, Environ{})
	if out.DBPath != "/file.db" {
		t.Errorf("DBPath=%q want /file.db", out.DBPath)
	}
	if out.ListenAddr != "127.0.0.1:9000" {
		t.Errorf("ListenAddr=%q want 127.0.0.1:9000", out.ListenAddr)
	}
}

func TestMergeOverrides_EmptyFlagDoesNotOverride(t *testing.T) {
	cfg := &Config{DBPath: "/file.db"}
	env := Environ{"ORCA_DB": "/env.db"}
	out := cfg.MergeOverrides(Flags{}, env)
	if out.DBPath != "/env.db" {
		t.Errorf("DBPath=%q want /env.db", out.DBPath)
	}
}

func TestMergeOverrides_EmptyEnvDoesNotOverride(t *testing.T) {
	cfg := &Config{DBPath: "/file.db"}
	env := Environ{"ORCA_DB": ""}
	out := cfg.MergeOverrides(Flags{}, env)
	if out.DBPath != "/file.db" {
		t.Errorf("DBPath=%q want /file.db", out.DBPath)
	}
}

func TestMergeOverrides_NodeCapacity(t *testing.T) {
	cfg := &Config{
		NodeCapacity: &CapacityConfig{CPU: 4, MemoryMB: 8192},
	}
	out := cfg.MergeOverrides(Flags{}, Environ{})
	if out.NodeCapacity == nil {
		t.Fatal("NodeCapacity nil")
	}
	if out.NodeCapacity.CPU != 4 {
		t.Errorf("CPU=%d want 4", out.NodeCapacity.CPU)
	}
	if out.NodeCapacity.MemoryMB != 8192 {
		t.Errorf("MemoryMB=%d want 8192", out.NodeCapacity.MemoryMB)
	}
	if cfg.NodeCapacity == out.NodeCapacity {
		t.Error("NodeCapacity not cloned")
	}
}

func TestMergeOverrides_NodeCapacityFlagAndEnv(t *testing.T) {
	cfg := &Config{NodeCapacity: &CapacityConfig{CPU: 4, MemoryMB: 8192}}
	flags := Flags{CPU: intPtr(8)}
	env := Environ{"ORCA_NODE_MEMORY_MB": "16384"}
	out := cfg.MergeOverrides(flags, env)
	if out.NodeCapacity.CPU != 8 {
		t.Errorf("CPU=%d want 8", out.NodeCapacity.CPU)
	}
	if out.NodeCapacity.MemoryMB != 16384 {
		t.Errorf("MemoryMB=%d want 16384", out.NodeCapacity.MemoryMB)
	}
}
