package cidr

import (
	"net"
	"reflect"
	"testing"
)

func TestGKECIDRDispatcher(t *testing.T) {
	type cidr struct {
		region                     string
		pod, node, service, master string
		isError                    bool

		// CIDR when set placeholder
		podP, nodeP, serviceP, masterP string
		isSetPrefixPlaceholderError    bool
	}
	cases := []cidr{
		{
			region:   "10.250.0.0/19",
			pod:      "10.250.0.0/21",
			node:     "10.250.8.0/23",
			service:  "10.250.12.0/23",
			master:   "10.250.14.0/28",
			podP:     "10.250.16.0/21",
			nodeP:    "10.250.24.0/23",
			serviceP: "10.250.28.0/23",
			masterP:  "10.250.30.0/28",
		},
		{
			region:                      "172.30.192.0/20",
			pod:                         "172.30.192.0/21",
			node:                        "172.30.200.0/23",
			service:                     "172.30.204.0/23",
			master:                      "172.30.206.0/28",
			isSetPrefixPlaceholderError: true,
		},
		{
			region:  "172.30.0.0/21",
			pod:     "",
			node:    "",
			service: "",
			master:  "",
			isError: true,
		},
	}

	opts := [][]Option{
		{},
		{
			AllocateMoreNATs(),
		},
	}
	for _, c := range cases {
		for _, opt := range opts {
			p, err := NewGKECIDRDispatcher(c.region, opt...)
			if err != nil {
				if !c.isError {
					t.Fatalf("unexpected error")
				}
				continue
			}
			t.Log("test default")
			p.Default()
			if p.PodIPv4CIDRBlock() != c.pod {
				t.Fatalf("pod IPv4 CIDR not correct, expected: %s, return: %s", c.pod, p.PodIPv4CIDRBlock())
			}
			if p.NodeIPv4CIDRBlock() != c.node {
				t.Fatalf("node IPv4 CIDR not correct, expected: %s, return: %s", c.node, p.NodeIPv4CIDRBlock())
			}
			if p.ServiceIPv4CIDRBlock() != c.service {
				t.Fatalf("service IPv4 CIDR not correct, expected: %s, return: %s", c.service, p.ServiceIPv4CIDRBlock())
			}
			if p.MasterIPv4CIDRBlock() != c.master {
				t.Fatalf("master IPv4 CIDR not correct, expected: %s, return: %s", c.master, p.MasterIPv4CIDRBlock())
			}

			// Set prefix placeholder
			t.Log("test set prefix placeholder")
			err = p.SetPrefixPlaceholder()
			if err != nil {
				if !c.isSetPrefixPlaceholderError {
					t.Fatalf("unexpected error")
				}
				continue
			}
			if p.PodIPv4CIDRBlock() != c.podP {
				t.Fatalf("pod IPv4 CIDR not correct, expected: %s, return: %s", c.pod, p.PodIPv4CIDRBlock())
			}
			if p.NodeIPv4CIDRBlock() != c.nodeP {
				t.Fatalf("node IPv4 CIDR not correct, expected: %s, return: %s", c.node, p.NodeIPv4CIDRBlock())
			}
			if p.ServiceIPv4CIDRBlock() != c.serviceP {
				t.Fatalf("service IPv4 CIDR not correct, expected: %s, return: %s", c.service, p.ServiceIPv4CIDRBlock())
			}
			if p.MasterIPv4CIDRBlock() != c.masterP {
				t.Fatalf("master IPv4 CIDR not correct, expected: %s, return: %s", c.master, p.MasterIPv4CIDRBlock())
			}
		}
	}
}

func TestSplitSubnet(t *testing.T) {
	testCases := []struct {
		cidr       string
		subnetSize int
		expected   int
	}{
		{"192.168.1.0/24", 8, 8},
	}

	for _, tc := range testCases {
		_, ipv4Net, _ := net.ParseCIDR(tc.cidr)

		subnets, _ := SplitSubnet(ipv4Net, tc.subnetSize)

		if len(subnets) != tc.expected {
			t.Errorf("failed split subnets, expected %d subnets,but got %d subnets", tc.expected, len(subnets))
		}
		if subnets[0].String() != "192.168.1.0/27" {
			t.Errorf("failed split subnets, expected 192.168.1.0/27,but got %s", subnets[0].String())
		}
		if subnets[7].String() != "192.168.1.224/27" {
			t.Errorf("failed split subnets, expected 192.168.1.224/27,but got %s", subnets[7].String())
		}
	}
}

func TestCutCIDR(t *testing.T) {
	testCases := []struct {
		cidr     string
		cutSize  int
		expected []string
		hasErr   bool
	}{
		{"10.0.0.0/16", 17, []string{"10.0.0.0/17", "10.0.128.0/17"}, false},
		{"10.0.0.0/16", 18, []string{"10.0.0.0/18", "10.0.64.0/18", "10.0.128.0/17"}, false},
		{"10.0.0.0/16", 19, []string{"10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/18", "10.0.128.0/17"}, false},
		{"10.0.0.0/16", 16, []string{"10.0.0.0/16"}, false},
		{"10.0.0.0/16", 15, nil, true},
	}

	for _, tc := range testCases {
		_, ipv4Net, _ := net.ParseCIDR(tc.cidr)

		cidrs, err := CutCIDR(ipv4Net, tc.cutSize)
		if err != nil && !tc.hasErr {
			t.Errorf("failed to cut cidr, unexpected error: %v", err)
		}

		if len(cidrs) != len(tc.expected) {
			t.Errorf("failed to cut cidr, expected %d cidrs,but got %d cidrs", len(tc.expected), len(cidrs))
		}
		for i, cidr := range cidrs {
			if cidr.String() != tc.expected[i] {
				t.Errorf("failed to cut cidr, expected %d cidr is %s,but got %s", i, tc.expected[i], cidr.String())
			}
		}
	}
}

func Test_GkeCidrAllocate(t *testing.T) {
	type cidr struct {
		region                     string
		pod, node, service, master string
		pscnat                     []string
	}
	cases := []cidr{
		{
			region:  "10.250.0.0/19",
			pod:     "10.250.0.0/20",
			node:    "10.250.16.0/22",
			service: "10.250.20.0/22",
			master:  "10.250.24.0/28",
			pscnat: []string{
				"10.250.24.128/25",
				"10.250.25.0/24",
			},
		},
		{
			region:  "10.250.0.0/16",
			pod:     "10.250.0.0/17",
			node:    "10.250.128.0/19",
			service: "10.250.160.0/19",
			master:  "10.250.192.0/28",
			pscnat: []string{
				"10.250.192.128/25",
				"10.250.193.0/24",
				"10.250.194.0/23",
				"10.250.196.0/22",
				"10.250.200.0/21",
			},
		},
		{
			region:  "10.0.0.0/16",
			pod:     "10.0.0.0/17",
			node:    "10.0.128.0/19",
			service: "10.0.160.0/19",
			master:  "10.0.192.0/28",
			pscnat: []string{
				"10.0.192.128/25",
				"10.0.193.0/24",
				"10.0.194.0/23",
				"10.0.196.0/22",
				"10.0.200.0/21",
			},
		},
	}

	opts := [][]Option{
		{},
		{
			AllocateMoreNATs(),
		},
	}

	for _, c := range cases {
		for _, opt := range opts {
			p, err := NewGKECIDRDispatcher(c.region, opt...)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			t.Log("test cidr allocate")
			err = p.Allocate()
			if err != nil {
				t.Fatal(err)
			}
			if p.PodIPv4CIDRBlock() != c.pod {
				t.Fatalf("pod IPv4 CIDR not correct, expected: %s, return: %s", c.pod, p.PodIPv4CIDRBlock())
			}
			if p.NodeIPv4CIDRBlock() != c.node {
				t.Fatalf("node IPv4 CIDR not correct, expected: %s, return: %s", c.node, p.NodeIPv4CIDRBlock())
			}
			if p.ServiceIPv4CIDRBlock() != c.service {
				t.Fatalf("service IPv4 CIDR not correct, expected: %s, return: %s", c.service, p.ServiceIPv4CIDRBlock())
			}
			if p.MasterIPv4CIDRBlock() != c.master {
				t.Fatalf("master IPv4 CIDR not correct, expected: %s, return: %s", c.master, p.MasterIPv4CIDRBlock())
			}
			if p.opt.AllocateMoreNATs {
				if !reflect.DeepEqual(p.PSCNATIPv4CIDRBlocks(), c.pscnat) {
					t.Fatalf("PSC NAT IPv4 CIDR not correct, expected: %v, return: %v", c.pscnat, p.PSCNATIPv4CIDRBlocks())
				}
			} else {
				if p.PSCNATIPv4CIDRBlocks()[0] != c.pscnat[0] {
					t.Fatalf("legacy: PSC NAT IPv4 CIDR not correct, expected: %v, return: %v", c.pscnat[0], p.PSCNATIPv4CIDRBlocks()[0])
				}
			}
		}
	}
}
