package clientip

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
)

func TestExtractSourceIP(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name       string
		header     string
		remoteAddr string
		want       string
	}{
		{
			name:       "single XFF entry trimmed",
			header:     "  198.51.100.7  ",
			remoteAddr: "10.0.0.1:5000",
			want:       "198.51.100.7",
		},
		{
			name:       "first XFF entry wins",
			header:     "198.51.100.7, 10.0.0.1, 192.168.1.1",
			remoteAddr: "10.0.0.1:5000",
			want:       "198.51.100.7",
		},
		{
			name:       "fallback to RemoteAddr without port",
			header:     "",
			remoteAddr: "203.0.113.42:65000",
			want:       "203.0.113.42",
		},
		{
			name:       "RemoteAddr without port preserved",
			header:     "",
			remoteAddr: "203.0.113.42",
			want:       "203.0.113.42",
		},
		{
			name:       "no header and no RemoteAddr returns empty",
			header:     "",
			remoteAddr: "",
			want:       "",
		},
	}

	gin.SetMode(gin.TestMode)
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()

			req := httptest.NewRequest(http.MethodGet, "/", nil)
			req.RemoteAddr = tc.remoteAddr
			if tc.header != "" {
				req.Header.Set("X-Forwarded-For", tc.header)
			}
			c, _ := gin.CreateTestContext(httptest.NewRecorder())
			c.Request = req

			got := ExtractSourceIP(c)
			if got != tc.want {
				t.Fatalf("ExtractSourceIP() = %q, want %q", got, tc.want)
			}
		})
	}
}

func TestExtractSourceIPNilSafety(t *testing.T) {
	t.Parallel()

	if got := ExtractSourceIP(nil); got != "" {
		t.Fatalf("nil context: want empty, got %q", got)
	}

	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	if got := ExtractSourceIP(c); got != "" {
		t.Fatalf("context with nil Request: want empty, got %q", got)
	}
}
