package auth

import (
	"encoding/hex"
	"os"
	"strings"
	"testing"
)

// ---------------------------------------------------------------------------
// extractDomainFromURL
// ---------------------------------------------------------------------------

func TestExtractDomainFromURL(t *testing.T) {
	tests := []struct {
		name  string
		input string
		want  string
	}{
		{
			name:  "https with domain only",
			input: "https://example.com",
			want:  "example.com",
		},
		{
			name:  "http with port and path",
			input: "http://example.com:8080/path",
			want:  "example.com",
		},
		{
			name:  "https with subdomain and path",
			input: "https://sub.domain.com/api/v1",
			want:  "sub.domain.com",
		},
		{
			name:  "no scheme bare domain",
			input: "example.com",
			want:  "example.com",
		},
		{
			name:  "https with IP and port",
			input: "https://192.168.1.1:443",
			want:  "192.168.1.1",
		},
		{
			name:  "empty string",
			input: "",
			want:  "",
		},
		{
			name:  "bare domain no scheme",
			input: "gateway.orama.network",
			want:  "gateway.orama.network",
		},
		{
			name:  "https with query params",
			input: "https://example.com?foo=bar",
			want:  "example.com",
		},
		{
			name:  "https with path and query params",
			input: "https://example.com/page?q=1&r=2",
			want:  "example.com",
		},
		{
			name:  "bare domain with port",
			input: "example.com:9090",
			want:  "example.com",
		},
		{
			name:  "https with fragment",
			input: "https://example.com/page#section",
			want:  "example.com",
		},
		{
			name:  "https with user info",
			input: "https://user:pass@example.com/path",
			want:  "example.com",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := extractDomainFromURL(tt.input)
			if got != tt.want {
				t.Errorf("extractDomainFromURL(%q) = %q, want %q", tt.input, got, tt.want)
			}
		})
	}
}

// ---------------------------------------------------------------------------
// ValidateWalletAddress
// ---------------------------------------------------------------------------

func TestValidateWalletAddress(t *testing.T) {
	validHex40 := "aabbccddee1122334455aabbccddee1122334455"

	tests := []struct {
		name    string
		address string
		want    bool
	}{
		{
			name:    "valid 40 char hex with 0x prefix",
			address: "0x" + validHex40,
			want:    true,
		},
		{
			name:    "valid 40 char hex without prefix",
			address: validHex40,
			want:    true,
		},
		{
			name:    "valid uppercase hex with 0x prefix",
			address: "0x" + strings.ToUpper(validHex40),
			want:    true,
		},
		{
			name:    "too short",
			address: "0xaabbccdd",
			want:    false,
		},
		{
			name:    "too long",
			address: "0x" + validHex40 + "ff",
			want:    false,
		},
		{
			name:    "non hex characters",
			address: "0x" + "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
			want:    false,
		},
		{
			name:    "empty string",
			address: "",
			want:    false,
		},
		{
			name:    "just 0x prefix",
			address: "0x",
			want:    false,
		},
		{
			name:    "39 hex chars with 0x prefix",
			address: "0x" + validHex40[:39],
			want:    false,
		},
		{
			name:    "41 hex chars with 0x prefix",
			address: "0x" + validHex40 + "a",
			want:    false,
		},
		{
			name:    "mixed case hex is valid",
			address: "0xAaBbCcDdEe1122334455aAbBcCdDeE1122334455",
			want:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := ValidateWalletAddress(tt.address)
			if got != tt.want {
				t.Errorf("ValidateWalletAddress(%q) = %v, want %v", tt.address, got, tt.want)
			}
		})
	}
}

// ---------------------------------------------------------------------------
// FormatWalletAddress
// ---------------------------------------------------------------------------

func TestFormatWalletAddress(t *testing.T) {
	tests := []struct {
		name    string
		address string
		want    string
	}{
		{
			name:    "already lowercase with 0x",
			address: "0xaabbccddee1122334455aabbccddee1122334455",
			want:    "0xaabbccddee1122334455aabbccddee1122334455",
		},
		{
			name:    "uppercase gets lowercased",
			address: "0xAABBCCDDEE1122334455AABBCCDDEE1122334455",
			want:    "0xaabbccddee1122334455aabbccddee1122334455",
		},
		{
			name:    "without 0x prefix gets it added",
			address: "aabbccddee1122334455aabbccddee1122334455",
			want:    "0xaabbccddee1122334455aabbccddee1122334455",
		},
		{
			name:    "0X uppercase prefix gets normalized",
			address: "0XAABBCCDDEE1122334455AABBCCDDEE1122334455",
			want:    "0xaabbccddee1122334455aabbccddee1122334455",
		},
		{
			name:    "mixed case gets normalized",
			address: "0xAaBbCcDdEe1122334455AaBbCcDdEe1122334455",
			want:    "0xaabbccddee1122334455aabbccddee1122334455",
		},
		{
			name:    "empty string gets 0x prefix",
			address: "",
			want:    "0x",
		},
		{
			name:    "just 0x stays as 0x",
			address: "0x",
			want:    "0x",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := FormatWalletAddress(tt.address)
			if got != tt.want {
				t.Errorf("FormatWalletAddress(%q) = %q, want %q", tt.address, got, tt.want)
			}
		})
	}
}

// ---------------------------------------------------------------------------
// GenerateRandomString
// ---------------------------------------------------------------------------

func TestGenerateRandomString(t *testing.T) {
	t.Run("returns correct length", func(t *testing.T) {
		lengths := []int{8, 16, 32, 64}
		for _, l := range lengths {
			s, err := GenerateRandomString(l)
			if err != nil {
				t.Fatalf("GenerateRandomString(%d) returned error: %v", l, err)
			}
			if len(s) != l {
				t.Errorf("GenerateRandomString(%d) returned string of length %d, want %d", l, len(s), l)
			}
		}
	})

	t.Run("two calls produce different values", func(t *testing.T) {
		s1, err := GenerateRandomString(32)
		if err != nil {
			t.Fatalf("first call returned error: %v", err)
		}
		s2, err := GenerateRandomString(32)
		if err != nil {
			t.Fatalf("second call returned error: %v", err)
		}
		if s1 == s2 {
			t.Errorf("two calls to GenerateRandomString(32) produced the same value: %q", s1)
		}
	})

	t.Run("returns hex characters only", func(t *testing.T) {
		s, err := GenerateRandomString(32)
		if err != nil {
			t.Fatalf("GenerateRandomString(32) returned error: %v", err)
		}
		// hex.DecodeString requires even-length input; pad if needed
		toDecode := s
		if len(toDecode)%2 != 0 {
			toDecode = toDecode + "0"
		}
		if _, err := hex.DecodeString(toDecode); err != nil {
			t.Errorf("GenerateRandomString(32) returned non-hex string: %q, err: %v", s, err)
		}
	})

	t.Run("length zero returns empty string", func(t *testing.T) {
		s, err := GenerateRandomString(0)
		if err != nil {
			t.Fatalf("GenerateRandomString(0) returned error: %v", err)
		}
		if s != "" {
			t.Errorf("GenerateRandomString(0) = %q, want empty string", s)
		}
	})

	t.Run("length one returns single hex char", func(t *testing.T) {
		s, err := GenerateRandomString(1)
		if err != nil {
			t.Fatalf("GenerateRandomString(1) returned error: %v", err)
		}
		if len(s) != 1 {
			t.Errorf("GenerateRandomString(1) returned string of length %d, want 1", len(s))
		}
		// Must be a valid hex character
		const hexChars = "0123456789abcdef"
		if !strings.Contains(hexChars, s) {
			t.Errorf("GenerateRandomString(1) = %q, not a valid hex character", s)
		}
	})
}

// ---------------------------------------------------------------------------
// phantomAuthURL
// ---------------------------------------------------------------------------

func TestPhantomAuthURL(t *testing.T) {
	t.Run("returns default when env var not set", func(t *testing.T) {
		// Ensure the env var is not set
		os.Unsetenv("ORAMA_PHANTOM_AUTH_URL")

		got := phantomAuthURL()
		if got != defaultPhantomAuthURL {
			t.Errorf("phantomAuthURL() = %q, want default %q", got, defaultPhantomAuthURL)
		}
	})

	t.Run("returns custom URL when env var is set", func(t *testing.T) {
		custom := "https://custom-phantom.example.com"
		os.Setenv("ORAMA_PHANTOM_AUTH_URL", custom)
		defer os.Unsetenv("ORAMA_PHANTOM_AUTH_URL")

		got := phantomAuthURL()
		if got != custom {
			t.Errorf("phantomAuthURL() = %q, want %q", got, custom)
		}
	})

	t.Run("trailing slash stripped from env var", func(t *testing.T) {
		custom := "https://custom-phantom.example.com/"
		os.Setenv("ORAMA_PHANTOM_AUTH_URL", custom)
		defer os.Unsetenv("ORAMA_PHANTOM_AUTH_URL")

		got := phantomAuthURL()
		want := "https://custom-phantom.example.com"
		if got != want {
			t.Errorf("phantomAuthURL() = %q, want %q (trailing slash should be stripped)", got, want)
		}
	})

	t.Run("multiple trailing slashes stripped from env var", func(t *testing.T) {
		custom := "https://custom-phantom.example.com///"
		os.Setenv("ORAMA_PHANTOM_AUTH_URL", custom)
		defer os.Unsetenv("ORAMA_PHANTOM_AUTH_URL")

		got := phantomAuthURL()
		want := "https://custom-phantom.example.com"
		if got != want {
			t.Errorf("phantomAuthURL() = %q, want %q (trailing slashes should be stripped)", got, want)
		}
	})
}
