package cidr

import (
	"net"
	"testing"
)

func TestAKSCIDRDispatcher(t *testing.T) {
	type cidr struct {
		name                                       string
		ipRange                                    string
		podCIDR, nodeCIDR, serviceCIDR, plsNATCIDR string
		apiServerCIDR                              string
		dnsIP                                      string
		isNewDispatcherError                       bool
		isAllocateError                            bool
	}

	cases := []cidr{
		{
			name:                 "invalid ip range, not an cidr",
			ipRange:              "10.250.0.0",
			isNewDispatcherError: true,
		},
		{
			name:                 "invalid ip range, not an IPv4 cidr",
			ipRange:              "2000::/10",
			isNewDispatcherError: true,
		},
		{
			name:          "ip range is /19",
			ipRange:       "10.250.0.0/19",
			podCIDR:       "10.250.0.0/20",
			nodeCIDR:      "10.250.16.0/21",
			serviceCIDR:   "10.250.24.0/22",
			plsNATCIDR:    "10.250.28.0/23",
			apiServerCIDR: "10.250.30.0/28",
			dnsIP:         "10.250.24.10",
		},
		{
			name:          "ip range is /20",
			ipRange:       "172.30.192.0/20",
			podCIDR:       "172.30.192.0/21",
			nodeCIDR:      "172.30.200.0/22",
			serviceCIDR:   "172.30.204.0/23",
			plsNATCIDR:    "172.30.206.0/24",
			apiServerCIDR: "172.30.207.0/28",
			dnsIP:         "172.30.204.10",
		},
	}

	for _, cs := range cases {
		p, err := NewAKSCIDRDispatcher(cs.ipRange)
		if err != nil {
			if !cs.isNewDispatcherError {
				t.Fatalf("%s, unexpected error", cs.name)
			}
			continue
		}
		if err := p.Allocate(); err != nil {
			if !cs.isAllocateError {
				t.Fatalf("%s, unexpected error", cs.name)
			}
			continue
		}
		if p.PodIPv4CIDRBlock() != cs.podCIDR {
			t.Fatalf("%s, pod IPv4 CIDR not correct, expected: %s, return: %s", cs.name, cs.podCIDR, p.PodIPv4CIDRBlock())
		}
		if p.NodeIPv4CIDRBlock() != cs.nodeCIDR {
			t.Fatalf("%s, node IPv4 CIDR not correct, expected: %s, return: %s", cs.name, cs.nodeCIDR, p.NodeIPv4CIDRBlock())
		}
		if p.ServiceIPv4CIDRBlock() != cs.serviceCIDR {
			t.Fatalf("%s, service IPv4 CIDR not correct, expected: %s, return: %s", cs.name, cs.serviceCIDR, p.ServiceIPv4CIDRBlock())
		}
		if p.DNSServiceIP() != cs.dnsIP {
			t.Fatalf("%s, dns IP not correct, expected: %s, return: %s", cs.name, cs.dnsIP, p.DNSServiceIP())
		}
		if p.PLSNATIPv4CIDRBlock() != cs.plsNATCIDR {
			t.Fatalf("%s, pls nat IPv4 CIDR not correct, expected: %s, return: %s", cs.name, cs.plsNATCIDR, p.PLSNATIPv4CIDRBlock())
		}
		if p.APIServerIPv4CIDRBlock() != cs.apiServerCIDR {
			t.Fatalf("%s, api server IPv4 CIDR not correct, expected: %s, return: %s", cs.name, cs.apiServerCIDR, p.APIServerIPv4CIDRBlock())
		}
	}
}

func TestGetDNSIPFromCIDR(t *testing.T) {
	testCases := []struct {
		cidr    string
		idx     uint32
		want    net.IP
		wantErr bool
	}{
		{"192.168.1.0/24", 10, net.ParseIP("192.168.1.10"), false},
		{"192.168.1.128/26", 10, net.ParseIP("192.168.1.138"), false},
		{"192.168.1.248/29", 10, nil, true}, // 258 is out of range
		{"10.0.0.0/8", 10, net.ParseIP("10.0.0.10"), false},
		{"0.0.0.0/0", 10, net.ParseIP("0.0.0.10"), false},

		{"127.0.0.1/32", 10, nil, true},
		{"192.168.1.0/33", 10, nil, true},
		{"abc.def.ghi.jkl/24", 10, nil, true},
	}

	for _, tc := range testCases {
		t.Run(tc.cidr, func(t *testing.T) {
			got, err := getDNSIPFromCIDR(tc.cidr, tc.idx)
			if (err != nil) != tc.wantErr {
				t.Errorf("getDNSIPFromCIDR(%q) error = %v, wantErr %v", tc.cidr, err, tc.wantErr)
				return
			}
			if !tc.wantErr && !got.Equal(tc.want) {
				t.Errorf("getDNSIPFromCIDR(%q) = %v, want %v", tc.cidr, got, tc.want)
			}
		})
	}
}
