package security

import (
	"strings"
	"testing"
)

func TestParseExactHostnameAllowlist(t *testing.T) {
	allow := ParseExactHostnameAllowlist(" crowdsec , CADDY, ,http://example.com,example.com/path,user@host,::1 ")

	if _, ok := allow["crowdsec"]; !ok {
		t.Fatalf("expected allowlist to contain crowdsec")
	}
	if _, ok := allow["caddy"]; !ok {
		t.Fatalf("expected allowlist to contain caddy")
	}
	if _, ok := allow["::1"]; !ok {
		t.Fatalf("expected allowlist to contain ::1")
	}

	if _, ok := allow["http://example.com"]; ok {
		t.Fatalf("expected scheme-containing entry to be ignored")
	}
	if _, ok := allow["example.com/path"]; ok {
		t.Fatalf("expected path-containing entry to be ignored")
	}
	if _, ok := allow["user@host"]; ok {
		t.Fatalf("expected userinfo-containing entry to be ignored")
	}
}

func TestValidateInternalServiceBaseURL(t *testing.T) {
	allowed := map[string]struct{}{"localhost": {}, "127.0.0.1": {}, "::1": {}}

	cases := []struct {
		name         string
		raw          string
		expectedPort int
		want         string
		wantErr      bool
		errContains  string
	}{
		{
			name:         "OK http localhost explicit port",
			raw:          "http://localhost:2019",
			expectedPort: 2019,
			want:         "http://localhost:2019",
		},
		{
			name:         "OK http localhost path normalized",
			raw:          "http://localhost:2019/config/",
			expectedPort: 2019,
			want:         "http://localhost:2019",
		},
		{
			name:         "OK https localhost default port",
			raw:          "https://localhost",
			expectedPort: 443,
			want:         "https://localhost:443",
		},
		{
			name:         "OK ipv6 loopback explicit port",
			raw:          "http://[::1]:2019",
			expectedPort: 2019,
			want:         "http://[::1]:2019",
		},
		{
			name:         "Reject userinfo",
			raw:          "http://user:pass@localhost:2019",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "embedded credentials",
		},
		{
			name:         "Reject unsupported scheme",
			raw:          "file://localhost:2019",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "unsupported scheme",
		},
		{
			name:         "Reject missing hostname",
			raw:          "http://:2019",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "missing hostname",
		},
		{
			name:         "Reject hostname not allowed",
			raw:          "http://evil.example:2019",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "hostname not allowed",
		},
		{
			name:         "Reject unexpected port when omitted",
			raw:          "http://localhost",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "unexpected port",
		},
		{
			name:         "Reject invalid port",
			raw:          "http://localhost:0",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "invalid port",
		},
		{
			name:         "Reject out-of-range port",
			raw:          "http://localhost:99999",
			expectedPort: 2019,
			wantErr:      true,
			errContains:  "invalid port",
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			u, err := ValidateInternalServiceBaseURL(tc.raw, tc.expectedPort, allowed)
			if tc.wantErr {
				if err == nil {
					t.Fatalf("expected error, got nil")
				}
				if tc.errContains != "" && !strings.Contains(err.Error(), tc.errContains) {
					t.Fatalf("expected error to contain %q, got %q", tc.errContains, err.Error())
				}
				return
			}

			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if u.String() != tc.want {
				t.Fatalf("expected %q, got %q", tc.want, u.String())
			}
		})
	}
}
