package gke

import (
	"context"
	"testing"

	gkev1beta1 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta1"
	upboundcontainerv1beta1 "github.com/upbound/provider-gcp/apis/container/v1beta1"
	"go.uber.org/mock/gomock"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/tidbcloud/infra-provider/pkg/cloud/gcp"
)

func Test_getNodeCount_contrib(t *testing.T) {
	t.Parallel()
	ctrl := gomock.NewController(t)
	t.Cleanup(ctrl.Finish)

	mockGCP := gcp.NewMockClient(ctrl)

	raw := &gkev1beta1.NodePool{
		ObjectMeta: metav1.ObjectMeta{Name: "np-contrib"},
	}
	raw.Status.AtProvider.InstanceGroupUrls = []string{
		"https://www.googleapis.com/compute/v1/projects/p1/zones/us-central1-a/instanceGroupManagers/ig1",
		"https://www.googleapis.com/compute/v1/projects/p1/zones/us-central1-b/instanceGroupManagers/ig2",
	}
	np := &contribNodePool{raw}

	mockGCP.EXPECT().GetInstanceGroupSize(gomock.Any(), "p1", "us-central1-a", "ig1").Return(int64(2), nil)
	mockGCP.EXPECT().GetInstanceGroupSize(gomock.Any(), "p1", "us-central1-b", "ig2").Return(int64(3), nil)

	got, err := getNodeCount(context.Background(), mockGCP, np)
	if err != nil {
		t.Fatalf("getNodeCount() unexpected err: %v", err)
	}
	if got != 5 {
		t.Fatalf("getNodeCount() = %d, want 5", got)
	}
}

func Test_getNodeCount_upbound(t *testing.T) {
	t.Parallel()
	ctrl := gomock.NewController(t)
	t.Cleanup(ctrl.Finish)

	mockGCP := gcp.NewMockClient(ctrl)

	raw := &upboundcontainerv1beta1.NodePool{
		ObjectMeta: metav1.ObjectMeta{Name: "np-upbound"},
	}
	raw.Status.AtProvider.InstanceGroupUrls = []*string{
		ptr("https://www.googleapis.com/compute/v1/projects/p1/zones/us-central1-a/instanceGroupManagers/ig1"),
		ptr("https://www.googleapis.com/compute/v1/projects/p1/zones/us-central1-b/instanceGroupManagers/ig2"),
	}
	np := &upboundNodePool{raw}

	mockGCP.EXPECT().GetInstanceGroupSize(gomock.Any(), "p1", "us-central1-a", "ig1").Return(int64(3), nil)
	mockGCP.EXPECT().GetInstanceGroupSize(gomock.Any(), "p1", "us-central1-b", "ig2").Return(int64(5), nil)

	got, err := getNodeCount(context.Background(), mockGCP, np)
	if err != nil {
		t.Fatalf("getNodeCount() unexpected err: %v", err)
	}
	if got != 8 {
		t.Fatalf("getNodeCount() = %d, want 8", got)
	}
}

func Test_getNodeCount_upbound_nil_url(t *testing.T) {
	t.Parallel()
	ctrl := gomock.NewController(t)
	t.Cleanup(ctrl.Finish)

	mockGCP := gcp.NewMockClient(ctrl)

	raw := &upboundcontainerv1beta1.NodePool{
		ObjectMeta: metav1.ObjectMeta{Name: "np-nil"},
	}
	raw.Status.AtProvider.InstanceGroupUrls = []*string{nil, nil}
	np := &upboundNodePool{raw}

	got, err := getNodeCount(context.Background(), mockGCP, np)
	if err != nil {
		t.Fatalf("getNodeCount() unexpected err: %v", err)
	}
	if got != 0 {
		t.Fatalf("getNodeCount() = %d, want 0", got)
	}
}

func Test_contribNodePool_SetVersion(t *testing.T) {
	raw := &gkev1beta1.NodePool{}
	np := &contribNodePool{raw}
	np.SetVersion("1.28")
	if raw.Spec.ForProvider.Version == nil || *raw.Spec.ForProvider.Version != "1.28" {
		t.Fatal("SetVersion did not update the underlying CR")
	}
}

func Test_upboundNodePool_SetVersion(t *testing.T) {
	raw := &upboundcontainerv1beta1.NodePool{}
	np := &upboundNodePool{raw}
	np.SetVersion("1.28")
	if raw.Spec.ForProvider.Version == nil || *raw.Spec.ForProvider.Version != "1.28" {
		t.Fatal("SetVersion did not update the underlying CR")
	}
}

func Test_upboundNodePool_GetInstanceGroupURLs(t *testing.T) {
	raw := &upboundcontainerv1beta1.NodePool{}
	raw.Status.AtProvider.InstanceGroupUrls = []*string{
		ptr("url1"),
		nil,
		ptr("url2"),
	}
	np := &upboundNodePool{raw}

	urls := np.GetInstanceGroupURLs()
	if len(urls) != 2 {
		t.Fatalf("GetInstanceGroupURLs() returned %d urls, want 2", len(urls))
	}
	if urls[0] != "url1" || urls[1] != "url2" {
		t.Fatalf("GetInstanceGroupURLs() = %v, want [url1, url2]", urls)
	}
}

func ptr(s string) *string {
	return &s
}
