package database

import (
	"net"
	"testing"
)

func TestIPToUint32(t *testing.T) {
	tests := []struct {
		name     string
		ip       string
		expected uint32
	}{
		{
			name:     "Simple IP",
			ip:       "192.168.1.1",
			expected: 3232235777, // 192<<24 + 168<<16 + 1<<8 + 1
		},
		{
			name:     "Minimum IP",
			ip:       "0.0.0.0",
			expected: 0,
		},
		{
			name:     "Maximum IP",
			ip:       "255.255.255.255",
			expected: 4294967295,
		},
		{
			name:     "10.0.0.0",
			ip:       "10.0.0.0",
			expected: 167772160,
		},
		{
			name:     "172.16.0.0",
			ip:       "172.16.0.0",
			expected: 2886729728,
		},
		{
			name:     "8.8.8.8",
			ip:       "8.8.8.8",
			expected: 134744072,
		},
		{
			name:     "1.2.3.4",
			ip:       "1.2.3.4",
			expected: 16909060,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ip := net.ParseIP(tt.ip)
			if ip == nil {
				t.Fatalf("Failed to parse IP: %s", tt.ip)
			}

			result := ipToUint32(ip)
			if result != tt.expected {
				t.Errorf("ipToUint32(%s) = %d, want %d", tt.ip, result, tt.expected)
			}

			// Test with IPv4-mapped IPv6 address
			ip6 := net.ParseIP(tt.ip).To16()
			if ip6 != nil {
				result6 := ipToUint32(ip6)
				if result6 != tt.expected {
					t.Errorf("ipToUint32(%s as IPv6) = %d, want %d", tt.ip, result6, tt.expected)
				}
			}
		})
	}
}

func TestCalculateIPv4Range(t *testing.T) {
	tests := []struct {
		name      string
		cidr      string
		wantStart uint32
		wantEnd   uint32
		wantErr   bool
	}{
		{
			name:      "Single IP /32",
			cidr:      "192.168.1.1/32",
			wantStart: 3232235777,
			wantEnd:   3232235777,
		},
		{
			name:      "Class C /24",
			cidr:      "192.168.1.0/24",
			wantStart: 3232235776, // 192.168.1.0
			wantEnd:   3232236031, // 192.168.1.255
		},
		{
			name:      "Class B /16",
			cidr:      "192.168.0.0/16",
			wantStart: 3232235520, // 192.168.0.0
			wantEnd:   3232301055, // 192.168.255.255
		},
		{
			name:      "Class A /8",
			cidr:      "10.0.0.0/8",
			wantStart: 167772160, // 10.0.0.0
			wantEnd:   184549375, // 10.255.255.255
		},
		{
			name:      "Entire IPv4 space /0",
			cidr:      "0.0.0.0/0",
			wantStart: 0,
			wantEnd:   4294967295,
		},
		{
			name:      "Small subnet /30",
			cidr:      "192.168.1.0/30",
			wantStart: 3232235776, // 192.168.1.0
			wantEnd:   3232235779, // 192.168.1.3
		},
		{
			name:      "Medium subnet /20",
			cidr:      "172.16.0.0/20",
			wantStart: 2886729728, // 172.16.0.0
			wantEnd:   2886733823, // 172.16.15.255
		},
		{
			name:      "Private range 172.16/12",
			cidr:      "172.16.0.0/12",
			wantStart: 2886729728, // 172.16.0.0
			wantEnd:   2887778303, // 172.31.255.255
		},
		{
			name:      "Google DNS /29",
			cidr:      "8.8.8.8/29",
			wantStart: 134744072, // 8.8.8.8 (network is actually 8.8.8.8 with /29)
			wantEnd:   134744079, // 8.8.8.15
		},
		{
			name:      "Non-zero host bits",
			cidr:      "192.168.1.5/24",
			wantStart: 3232235776, // 192.168.1.0 (network address)
			wantEnd:   3232236031, // 192.168.1.255
		},
		{
			name:    "Invalid CIDR",
			cidr:    "192.168.1.1/33",
			wantErr: true,
		},
		{
			name:    "Invalid IP",
			cidr:    "256.256.256.256/24",
			wantErr: true,
		},
		{
			name:    "IPv6 CIDR",
			cidr:    "2001:db8::/32",
			wantErr: true,
		},
		{
			name:    "Empty CIDR",
			cidr:    "",
			wantErr: true,
		},
		{
			name:    "Missing mask",
			cidr:    "192.168.1.1",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			start, end, err := CalculateIPv4Range(tt.cidr)

			if tt.wantErr {
				if err == nil {
					t.Errorf("CalculateIPv4Range(%s) expected error, got nil", tt.cidr)
				}
				return
			}

			if err != nil {
				t.Errorf("CalculateIPv4Range(%s) unexpected error: %v", tt.cidr, err)
				return
			}

			if start != tt.wantStart {
				t.Errorf("CalculateIPv4Range(%s) start = %d, want %d", tt.cidr, start, tt.wantStart)
			}

			if end != tt.wantEnd {
				t.Errorf("CalculateIPv4Range(%s) end = %d, want %d", tt.cidr, end, tt.wantEnd)
			}

			// Verify that start <= end
			if start > end {
				t.Errorf("CalculateIPv4Range(%s) start (%d) > end (%d)", tt.cidr, start, end)
			}

			// Verify the range size matches the CIDR mask
			if !tt.wantErr && tt.cidr != "" {
				_, ipNet, _ := net.ParseCIDR(tt.cidr)
				if ipNet != nil {
					ones, bits := ipNet.Mask.Size()
					expectedSize := uint32(1) << uint(bits-ones)
					actualSize := end - start + 1
					if actualSize != expectedSize {
						t.Errorf("CalculateIPv4Range(%s) range size = %d, want %d", tt.cidr, actualSize, expectedSize)
					}
				}
			}
		})
	}
}

func TestIPv4RangeIntegration(t *testing.T) {
	// Test that our functions work correctly together
	tests := []struct {
		name          string
		cidr          string
		testIPs       []string
		shouldContain []bool
	}{
		{
			name: "192.168.1.0/24",
			cidr: "192.168.1.0/24",
			testIPs: []string{
				"192.168.1.0",
				"192.168.1.1",
				"192.168.1.255",
				"192.168.0.255",
				"192.168.2.0",
			},
			shouldContain: []bool{true, true, true, false, false},
		},
		{
			name: "10.0.0.0/8",
			cidr: "10.0.0.0/8",
			testIPs: []string{
				"10.0.0.0",
				"10.255.255.255",
				"10.1.2.3",
				"9.255.255.255",
				"11.0.0.0",
			},
			shouldContain: []bool{true, true, true, false, false},
		},
		{
			name: "172.16.0.0/12",
			cidr: "172.16.0.0/12",
			testIPs: []string{
				"172.16.0.0",
				"172.31.255.255",
				"172.20.1.1",
				"172.15.255.255",
				"172.32.0.0",
			},
			shouldContain: []bool{true, true, true, false, false},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			start, end, err := CalculateIPv4Range(tt.cidr)
			if err != nil {
				t.Fatalf("Failed to calculate range for %s: %v", tt.cidr, err)
			}

			for i, testIP := range tt.testIPs {
				ip := net.ParseIP(testIP)
				if ip == nil {
					t.Fatalf("Failed to parse test IP: %s", testIP)
				}

				ipUint := ipToUint32(ip)
				contained := ipUint >= start && ipUint <= end

				if contained != tt.shouldContain[i] {
					t.Errorf("IP %s in range %s: got %v, want %v", testIP, tt.cidr, contained, tt.shouldContain[i])
				}
			}
		})
	}
}

func BenchmarkIPToUint32(b *testing.B) {
	ip := net.ParseIP("192.168.1.1")
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_ = ipToUint32(ip)
	}
}

func BenchmarkCalculateIPv4Range(b *testing.B) {
	cidr := "192.168.0.0/16"
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		_, _, _ = CalculateIPv4Range(cidr)
	}
}
