package config

import (
	"encoding/base64"
	"strings"
	"testing"
)

// validEnv sets the minimum required variables for a passing Load.
func validEnv(t *testing.T) {
	t.Helper()
	t.Setenv("SESSION_SECRET", "0123456789abcdef0123456789abcdef")
	t.Setenv("CREDENTIALS_ENC_KEY", base64.StdEncoding.EncodeToString(make([]byte, 32)))
}

func TestLoadDefaults(t *testing.T) {
	validEnv(t)
	c, err := Load()
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if c.AppPort != 8787 {
		t.Errorf("AppPort = %d, want 8787", c.AppPort)
	}
	if c.MongoDB != "bountyboard" {
		t.Errorf("MongoDB = %q", c.MongoDB)
	}
	if c.AtomizerTimeout.Seconds() != 120 {
		t.Errorf("AtomizerTimeout = %v", c.AtomizerTimeout)
	}
	if c.CookieSecure() {
		t.Error("CookieSecure should be false for http base URL in auto mode")
	}
	if len(c.CredentialsEncKey) != 32 {
		t.Errorf("CredentialsEncKey len = %d", len(c.CredentialsEncKey))
	}
}

func TestLoadValidationErrors(t *testing.T) {
	tests := []struct {
		name    string
		key     string
		value   string
		wantSub string
	}{
		{"bad port", "APP_PORT", "99999", "APP_PORT"},
		{"non-numeric port", "APP_PORT", "abc", "APP_PORT"},
		{"short session secret", "SESSION_SECRET", "short", "SESSION_SECRET"},
		{"bad enc key", "CREDENTIALS_ENC_KEY", "not-base64!!", "CREDENTIALS_ENC_KEY"},
		{"wrong size enc key", "CREDENTIALS_ENC_KEY", base64.StdEncoding.EncodeToString(make([]byte, 16)), "CREDENTIALS_ENC_KEY"},
		{"bad base url", "APP_BASE_URL", "not a url", "APP_BASE_URL"},
		{"bad atomizer url", "ATOMIZER_BASE_URL", "::::", "ATOMIZER_BASE_URL"},
		{"bad cidr", "TRUSTED_PROXY_CIDRS", "10.0.0.0/8,banana", "TRUSTED_PROXY_CIDRS"},
		{"bad cookie mode", "COOKIE_SECURE", "maybe", "COOKIE_SECURE"},
		{"bad upload size", "MAX_UPLOAD_MB", "0", "MAX_UPLOAD_MB"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			validEnv(t)
			t.Setenv(tt.key, tt.value)
			_, err := Load()
			if err == nil {
				t.Fatal("want error, got nil")
			}
			if !strings.Contains(err.Error(), tt.wantSub) {
				t.Errorf("error %q does not mention %q", err, tt.wantSub)
			}
		})
	}
}

func TestLoadMissingEncKey(t *testing.T) {
	t.Setenv("SESSION_SECRET", "0123456789abcdef0123456789abcdef")
	t.Setenv("CREDENTIALS_ENC_KEY", "")
	if _, err := Load(); err == nil {
		t.Fatal("want error for missing CREDENTIALS_ENC_KEY")
	}
}

func TestTrustedProxyCIDRs(t *testing.T) {
	validEnv(t)
	t.Setenv("TRUSTED_PROXY_CIDRS", "172.16.0.0/12, 10.0.0.0/8")
	c, err := Load()
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if len(c.TrustedProxyCIDRs) != 2 {
		t.Fatalf("got %d prefixes, want 2", len(c.TrustedProxyCIDRs))
	}
}

func TestCookieSecureModes(t *testing.T) {
	tests := []struct {
		mode, baseURL string
		want          bool
	}{
		{"auto", "http://localhost:8787", false},
		{"auto", "https://bounty.example.com", true},
		{"true", "http://localhost:8787", true},
		{"false", "https://bounty.example.com", false},
	}
	for _, tt := range tests {
		validEnv(t)
		t.Setenv("COOKIE_SECURE", tt.mode)
		t.Setenv("APP_BASE_URL", tt.baseURL)
		c, err := Load()
		if err != nil {
			t.Fatalf("Load: %v", err)
		}
		if got := c.CookieSecure(); got != tt.want {
			t.Errorf("mode=%s url=%s: CookieSecure()=%v, want %v", tt.mode, tt.baseURL, got, tt.want)
		}
	}
}

func TestOIDCAndSMTPEnabled(t *testing.T) {
	validEnv(t)
	c, err := Load()
	if err != nil {
		t.Fatal(err)
	}
	if c.OIDC.Enabled() || c.SMTP.Enabled() {
		t.Error("OIDC and SMTP must be disabled when unset")
	}

	t.Setenv("OIDC_ISSUER_URL", "https://issuer.example.com")
	t.Setenv("OIDC_CLIENT_ID", "id")
	t.Setenv("OIDC_CLIENT_SECRET", "secret")
	t.Setenv("SMTP_HOST", "smtp.example.com")
	c, err = Load()
	if err != nil {
		t.Fatal(err)
	}
	if !c.OIDC.Enabled() || !c.SMTP.Enabled() {
		t.Error("OIDC and SMTP must be enabled when set")
	}
}
