package service

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

	"github.com/pocket-id/pocket-id/backend/internal/common"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGeoLiteService_IPv6LocalRanges(t *testing.T) {
	tests := []struct {
		name            string
		localRanges     string
		testIP          string
		expectedCountry string
		expectedCity    string
		expectError     bool
	}{
		{
			name:            "IPv6 in local range",
			localRanges:     "2001:0db8:abcd:000::/56,2001:0db8:abcd:001::/56",
			testIP:          "2001:0db8:abcd:000::1",
			expectedCountry: "Internal Network",
			expectedCity:    "LAN",
			expectError:     false,
		},
		{
			name:        "IPv6 not in local range",
			localRanges: "2001:0db8:abcd:000::/56",
			testIP:      "2001:0db8:ffff:000::1",
			expectError: true,
		},
		{
			name:            "Multiple ranges - second range match",
			localRanges:     "2001:0db8:abcd:000::/56,2001:0db8:abcd:001::/56",
			testIP:          "2001:0db8:abcd:001::1",
			expectedCountry: "Internal Network",
			expectedCity:    "LAN",
			expectError:     false,
		},
		{
			name:        "Empty local ranges",
			localRanges: "",
			testIP:      "2001:0db8:abcd:000::1",
			expectError: true,
		},
		{
			name:            "IPv4 private address still works",
			localRanges:     "2001:0db8:abcd:000::/56",
			testIP:          "192.168.1.1",
			expectedCountry: "Internal Network",
			expectedCity:    "LAN",
			expectError:     false,
		},
		{
			name:            "IPv6 loopback",
			localRanges:     "2001:0db8:abcd:000::/56",
			testIP:          "::1",
			expectedCountry: "Internal Network",
			expectedCity:    "localhost",
			expectError:     false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalConfig := common.EnvConfig.LocalIPv6Ranges
			common.EnvConfig.LocalIPv6Ranges = tt.localRanges
			defer func() {
				common.EnvConfig.LocalIPv6Ranges = originalConfig
			}()

			service := NewGeoLiteService(&http.Client{})

			country, city, err := service.GetLocationByIP(tt.testIP)

			if tt.expectError {
				if err == nil && country != "Internal Network" {
					t.Errorf("Expected error or internal network classification for external IP")
				}
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.expectedCountry, country)
				assert.Equal(t, tt.expectedCity, city)
			}
		})
	}
}

func TestGeoLiteService_isLocalIPv6(t *testing.T) {
	tests := []struct {
		name        string
		localRanges string
		testIP      string
		expected    bool
	}{
		{
			name:        "Valid IPv6 in range",
			localRanges: "2001:0db8:abcd:000::/56",
			testIP:      "2001:0db8:abcd:000::1",
			expected:    true,
		},
		{
			name:        "Valid IPv6 not in range",
			localRanges: "2001:0db8:abcd:000::/56",
			testIP:      "2001:0db8:ffff:000::1",
			expected:    false,
		},
		{
			name:        "IPv4 address should return false",
			localRanges: "2001:0db8:abcd:000::/56",
			testIP:      "192.168.1.1",
			expected:    false,
		},
		{
			name:        "No ranges configured",
			localRanges: "",
			testIP:      "2001:0db8:abcd:000::1",
			expected:    false,
		},
		{
			name:        "Edge of range",
			localRanges: "2001:0db8:abcd:000::/56",
			testIP:      "2001:0db8:abcd:00ff:ffff:ffff:ffff:ffff",
			expected:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalConfig := common.EnvConfig.LocalIPv6Ranges
			common.EnvConfig.LocalIPv6Ranges = tt.localRanges
			defer func() {
				common.EnvConfig.LocalIPv6Ranges = originalConfig
			}()

			service := NewGeoLiteService(&http.Client{})
			ip := net.ParseIP(tt.testIP)
			if ip == nil {
				t.Fatalf("Invalid test IP: %s", tt.testIP)
			}

			result := service.isLocalIPv6(ip)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestGeoLiteService_initializeIPv6LocalRanges(t *testing.T) {
	tests := []struct {
		name        string
		envValue    string
		expectError bool
		expectCount int
	}{
		{
			name:        "Valid IPv6 ranges",
			envValue:    "2001:0db8:abcd:000::/56,2001:0db8:abcd:001::/56",
			expectError: false,
			expectCount: 2,
		},
		{
			name:        "Empty environment variable",
			envValue:    "",
			expectError: false,
			expectCount: 0,
		},
		{
			name:        "Invalid CIDR notation",
			envValue:    "2001:0db8:abcd:000::/999",
			expectError: true,
			expectCount: 0,
		},
		{
			name:        "IPv4 range in IPv6 env var",
			envValue:    "192.168.1.0/24",
			expectError: true,
			expectCount: 0,
		},
		{
			name:        "Mixed valid and invalid ranges",
			envValue:    "2001:0db8:abcd:000::/56,invalid-range",
			expectError: true,
			expectCount: 0,
		},
		{
			name:        "Whitespace handling",
			envValue:    " 2001:0db8:abcd:000::/56 , 2001:0db8:abcd:001::/56 ",
			expectError: false,
			expectCount: 2,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			originalConfig := common.EnvConfig.LocalIPv6Ranges
			common.EnvConfig.LocalIPv6Ranges = tt.envValue
			defer func() {
				common.EnvConfig.LocalIPv6Ranges = originalConfig
			}()

			service := &GeoLiteService{
				httpClient: &http.Client{},
			}

			err := service.initializeIPv6LocalRanges()

			if tt.expectError {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
			}

			assert.Len(t, service.localIPv6Ranges, tt.expectCount)
		})
	}
}
