package config_test

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

	"github.com/uopi/uopi/internal/config"
)

func TestDefault(t *testing.T) {
	cfg := config.Default()
	if cfg.Server.Listen == "" {
		t.Error("Default Listen must not be empty")
	}
	if cfg.Server.StorageDir == "" {
		t.Error("Default StorageDir must not be empty")
	}
	if !cfg.Datasource.Stub.Enabled {
		t.Error("Stub should be enabled by default")
	}
	if !cfg.Datasource.EPICS.Enabled {
		t.Error("EPICS should be enabled by default")
	}
	if !cfg.Datasource.Synthetic.Enabled {
		t.Error("Synthetic should be enabled by default")
	}
}

func TestLoadEmptyPath(t *testing.T) {
	// Empty path should return defaults without error.
	cfg, err := config.Load("")
	if err != nil {
		t.Fatalf("Load with empty path: %v", err)
	}
	if cfg.Server.Listen != config.Default().Server.Listen {
		t.Errorf("Listen = %q, want default %q", cfg.Server.Listen, config.Default().Server.Listen)
	}
}

func TestLoadTOML(t *testing.T) {
	toml := `
[server]
listen = ":9090"
storage_dir = "/tmp/uopi-test"

[datasource.stub]
enabled = false

[datasource.epics]
enabled = false
ca_addr_list = "192.168.1.1"
`
	f := filepath.Join(t.TempDir(), "uopi.toml")
	if err := os.WriteFile(f, []byte(toml), 0o644); err != nil {
		t.Fatal(err)
	}

	cfg, err := config.Load(f)
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.Server.Listen != ":9090" {
		t.Errorf("Listen = %q, want :9090", cfg.Server.Listen)
	}
	if cfg.Server.StorageDir != "/tmp/uopi-test" {
		t.Errorf("StorageDir = %q", cfg.Server.StorageDir)
	}
	if cfg.Datasource.Stub.Enabled {
		t.Error("Stub.Enabled should be false")
	}
	if cfg.Datasource.EPICS.Enabled {
		t.Error("EPICS.Enabled should be false")
	}
	if cfg.Datasource.EPICS.CAAddrList != "192.168.1.1" {
		t.Errorf("CAAddrList = %q", cfg.Datasource.EPICS.CAAddrList)
	}
}

func TestLoadBadPath(t *testing.T) {
	_, err := config.Load("/no/such/file.toml")
	if err == nil {
		t.Error("expected error for missing file")
	}
}

func TestEnvOverrides(t *testing.T) {
	t.Setenv("UOPI_SERVER_LISTEN", ":7777")
	t.Setenv("UOPI_SERVER_STORAGE_DIR", "/env/storage")
	t.Setenv("UOPI_EPICS_CA_ADDR_LIST", "10.0.0.1 10.0.0.2")
	t.Setenv("UOPI_EPICS_ARCHIVE_URL", "http://archive/")
	t.Setenv("UOPI_EPICS_CHANNEL_FINDER_URL", "http://cf/")

	cfg, err := config.Load("")
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.Server.Listen != ":7777" {
		t.Errorf("Listen = %q, want :7777", cfg.Server.Listen)
	}
	if cfg.Server.StorageDir != "/env/storage" {
		t.Errorf("StorageDir = %q", cfg.Server.StorageDir)
	}
	if cfg.Datasource.EPICS.CAAddrList != "10.0.0.1 10.0.0.2" {
		t.Errorf("CAAddrList = %q", cfg.Datasource.EPICS.CAAddrList)
	}
	if cfg.Datasource.EPICS.ArchiveURL != "http://archive/" {
		t.Errorf("ArchiveURL = %q", cfg.Datasource.EPICS.ArchiveURL)
	}
	if cfg.Datasource.EPICS.ChannelFinderURL != "http://cf/" {
		t.Errorf("ChannelFinderURL = %q", cfg.Datasource.EPICS.ChannelFinderURL)
	}
}

func TestEnvOverridesAuthTLSAndMisc(t *testing.T) {
	t.Setenv("UOPI_SERVER_MAX_UPDATE_RATE_HZ", "25.5")
	t.Setenv("UOPI_SERVER_TRUSTED_USER_HEADER", "X-Forwarded-User")
	t.Setenv("UOPI_SERVER_DEFAULT_USER", "svc")
	t.Setenv("UOPI_SERVER_KERBEROS_ENABLED", "true")
	t.Setenv("UOPI_SERVER_KERBEROS_KEYTAB", "/etc/krb.keytab")
	t.Setenv("UOPI_SERVER_KERBEROS_SERVICE_PRINCIPAL", "HTTP/host")
	t.Setenv("UOPI_SERVER_BASIC_AUTH_ENABLED", "YES")
	t.Setenv("UOPI_SERVER_BASIC_AUTH_PAM_SERVICE", "login")
	t.Setenv("UOPI_SERVER_TLS_ENABLED", "true")
	t.Setenv("UOPI_SERVER_TLS_CERT", "/c.pem")
	t.Setenv("UOPI_SERVER_TLS_KEY", "/k.pem")
	t.Setenv("UOPI_SERVER_LDAP_ENABLED", "true")
	t.Setenv("UOPI_SERVER_LDAP_URI", "ldaps://a ldaps://b")
	t.Setenv("UOPI_SERVER_LDAP_SEARCH_BASE", "dc=x,dc=y")
	t.Setenv("UOPI_SERVER_LDAP_BIND_DN", "cn=svc")
	t.Setenv("UOPI_SERVER_LDAP_BIND_PASSWORD", "secret")
	t.Setenv("UOPI_AUDIT_ENABLED", "true")
	t.Setenv("UOPI_AUDIT_DB_PATH", "/audit.db")
	t.Setenv("UOPI_EPICS_AUTO_SYNC_FILTER", "area=SR")
	t.Setenv("UOPI_EPICS_AUTO_SYNC_FROM_ARCHIVER", "true")
	t.Setenv("EPICS_PVA_ADDR_LIST", "10.1.1.1 10.1.1.2")
	t.Setenv("UOPI_UI_DEFAULT_ZOOM", "1.5")

	cfg, err := config.Load("")
	if err != nil {
		t.Fatalf("Load: %v", err)
	}

	if cfg.Server.MaxUpdateRateHz != 25.5 {
		t.Errorf("MaxUpdateRateHz = %v, want 25.5", cfg.Server.MaxUpdateRateHz)
	}
	if cfg.Server.TrustedUserHeader != "X-Forwarded-User" {
		t.Errorf("TrustedUserHeader = %q", cfg.Server.TrustedUserHeader)
	}
	if cfg.Server.DefaultUser != "svc" {
		t.Errorf("DefaultUser = %q", cfg.Server.DefaultUser)
	}
	if !cfg.Server.Kerberos.Enabled || cfg.Server.Kerberos.Keytab != "/etc/krb.keytab" || cfg.Server.Kerberos.ServicePrincipal != "HTTP/host" {
		t.Errorf("Kerberos = %+v", cfg.Server.Kerberos)
	}
	if !cfg.Server.BasicAuth.Enabled || cfg.Server.BasicAuth.PAMService != "login" {
		t.Errorf("BasicAuth = %+v", cfg.Server.BasicAuth)
	}
	if !cfg.Server.TLS.Enabled || cfg.Server.TLS.Cert != "/c.pem" || cfg.Server.TLS.Key != "/k.pem" {
		t.Errorf("TLS = %+v", cfg.Server.TLS)
	}
	if !cfg.Server.LDAP.Enabled || len(cfg.Server.LDAP.URIs) != 2 ||
		cfg.Server.LDAP.SearchBase != "dc=x,dc=y" || cfg.Server.LDAP.BindDN != "cn=svc" ||
		cfg.Server.LDAP.BindPassword != "secret" {
		t.Errorf("LDAP = %+v", cfg.Server.LDAP)
	}
	if !cfg.Audit.Enabled || cfg.Audit.DBPath != "/audit.db" {
		t.Errorf("Audit = %+v", cfg.Audit)
	}
	if cfg.Datasource.EPICS.AutoSyncFilter != "area=SR" || !cfg.Datasource.EPICS.AutoSyncFromArchiver {
		t.Errorf("EPICS sync = %+v", cfg.Datasource.EPICS)
	}
	if len(cfg.Datasource.PVA.AddrList) != 2 {
		t.Errorf("PVA AddrList = %+v", cfg.Datasource.PVA.AddrList)
	}
	if cfg.UI.DefaultZoom != 1.5 {
		t.Errorf("UI.DefaultZoom = %v, want 1.5", cfg.UI.DefaultZoom)
	}
}

func TestEnvInvalidNumbersIgnored(t *testing.T) {
	t.Setenv("UOPI_SERVER_MAX_UPDATE_RATE_HZ", "not-a-number")
	t.Setenv("UOPI_UI_DEFAULT_ZOOM", "xyz")
	cfg, err := config.Load("")
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	// Invalid floats are ignored, leaving the defaults intact.
	if cfg.Server.MaxUpdateRateHz != config.Default().Server.MaxUpdateRateHz {
		t.Errorf("invalid MaxUpdateRateHz should be ignored, got %v", cfg.Server.MaxUpdateRateHz)
	}
	if cfg.UI.DefaultZoom != config.Default().UI.DefaultZoom {
		t.Errorf("invalid DefaultZoom should be ignored, got %v", cfg.UI.DefaultZoom)
	}
}

func TestEnvTrimsWhitespace(t *testing.T) {
	t.Setenv("UOPI_SERVER_LISTEN", "  :8888  ")
	cfg, err := config.Load("")
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if cfg.Server.Listen != ":8888" {
		t.Errorf("Listen = %q, expected whitespace trimmed", cfg.Server.Listen)
	}
}
