package gke

import (
	"fmt"
	"testing"

	gcpcompute "google.golang.org/api/compute/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/privatelinkservice/task"
)

func TestFindSubnetwork(t *testing.T) {
	cases := []struct {
		desc        string
		prefixes    []string
		subnetworks []*gcpcompute.Subnetwork

		isLegacy bool

		expected string
		err      error
	}{
		{
			desc: "one prefix, no subnetworks",
			prefixes: []string{
				"192.168.0.128/25",
			},
			expected: "192.168.0.128/28",
		},
		{
			desc: "one prefix, subnetwork exists",
			prefixes: []string{
				"192.168.0.128/25",
			},
			subnetworks: []*gcpcompute.Subnetwork{
				{
					Name:        "tidbtest-psc-nat",
					IpCidrRange: "192.168.0.128/28",
				},
			},
			expected: "192.168.0.128/28",
		},
		{
			desc: "one prefix, subnetwork doesn't exist",
			prefixes: []string{
				"192.168.0.128/25",
			},
			subnetworks: []*gcpcompute.Subnetwork{
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.128/28",
				},
			},
			expected: "192.168.0.144/28",
		},
		{
			desc: "2 prefixes, more than 8 subnetworks",
			prefixes: []string{
				"192.168.0.128/25",
				"192.168.1.0/24",
			},
			subnetworks: []*gcpcompute.Subnetwork{
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.128/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.144/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.160/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.176/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.192/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.208/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.224/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.240/28",
				},
			},
			expected: "192.168.1.0/28",
		},
		{
			desc: "legacy, 2 prefixes, 8 subnetworks, used up",
			prefixes: []string{
				"192.168.0.128/25",
				"192.168.1.0/24",
			},
			subnetworks: []*gcpcompute.Subnetwork{
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.128/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.144/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.160/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.176/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.192/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.208/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.224/28",
				},
				{
					Name:        "tidbxxx-psc-nat",
					IpCidrRange: "192.168.0.240/28",
				},
			},
			isLegacy: true,
			err:      fmt.Errorf("nat subnet has been used up"),
		},
	}

	for i := range cases {
		c := &cases[i]
		t.Run(c.desc, func(t *testing.T) {
			task := NewNATSubnet(&task.Context{
				PrivateLinkService: &v1alpha1.PrivateLinkService{
					ObjectMeta: metav1.ObjectMeta{
						Name:        "test",
						Annotations: map[string]string{},
					},
				},
			}, nil)
			var cidr string
			var err error
			if c.isLegacy {
				cidr, err = task.legacyFindNATSubnetwork(c.prefixes[0], c.subnetworks)
			} else {
				cidr, err = task.findNATSubnetwork(c.prefixes, c.subnetworks)
			}
			if err != nil {
				if c.err == nil {
					t.Errorf("findNATSubnetwork() returns unexpected err: %v", err)
				}
				if err.Error() != c.err.Error() {
					t.Errorf("findNATSubnetwork() err is not expected, expected %v, actual: %v", c.err, err)
				}
			}
			if cidr != c.expected {
				t.Errorf("findNATSubnetwork() = %v, expected %v", cidr, c.expected)
			}
		})
	}
}
