package cluster

import (
	"context"
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	regionalpb "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/constants"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/models"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
	rsconfig "github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	mockAccountRsc "github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockaccount"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockappbox"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockappcontroller"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockapproute"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockinternet"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mocknamespace"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockvpc"
	mockAccountSvc "github.com/tidbcloud/infra-provider/regional-server/test/service/mockaccount"
	"go.uber.org/mock/gomock"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	apischema "k8s.io/apimachinery/pkg/runtime/schema"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func TestDeleteCluster(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockapproute := mockapproute.NewMockAPPRouteReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	mockSharedBucketSvc := mockAccountSvc.NewMockSharedBucketServiceInterface(ctrl)
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		nil,
		nil,
		nil,
		mockapproute,
		nil,
		nil,
		mockSharedBucketSvc,
		nil,
		cfg,
		tmplcfg,
	)
	t.Run("Delete Cluster Successfully", func(t *testing.T) {
		namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
		mockSharedBucketSvc.EXPECT().UnassignSharedBucket(gomock.Any(), "test").Return(nil)
		mockapproute.EXPECT().Delete(gomock.Any(), &v1alpha1.APPRoute{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: namer.GetNamespace(),
				Name:      namer.GetAPPRouteName(),
			},
		}).Return(nil)
		_, err := svc.Delete(context.TODO(), &regionalpb.DeleteClusterRequest{
			ClusterId: "test",
		})

		assert.NoError(t, err)
	})
	t.Run("Cluster Not Found", func(t *testing.T) {
		namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
		mockSharedBucketSvc.EXPECT().UnassignSharedBucket(gomock.Any(), "test").Return(nil)
		mockapproute.EXPECT().Delete(gomock.Any(), &v1alpha1.APPRoute{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: namer.GetNamespace(),
				Name:      namer.GetAPPRouteName(),
			},
		}).Return(apierrors.NewNotFound(apischema.GroupResource{}, ""))
		_, err := svc.Delete(context.TODO(), &regionalpb.DeleteClusterRequest{
			ClusterId: "test",
		})

		assert.ErrorContains(t, err, "not found")
	})
	//delete failed
	t.Run("Deletion Failed", func(t *testing.T) {
		inernalerr := apierrors.NewInternalError(fmt.Errorf("test-error"))
		mockSharedBucketSvc.EXPECT().UnassignSharedBucket(gomock.Any(), "test").Return(nil)
		mockapproute.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(inernalerr)
		_, err := svc.Delete(context.TODO(), &regionalpb.DeleteClusterRequest{
			ClusterId: "test",
		})
		assert.ErrorIs(t, err, inernalerr)
	})
}

func TestCreateAPPRoute(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockapproute := mockapproute.NewMockAPPRouteReaderWriter(ctrl)
	sharedBucketRef := &v1alpha1.SharedBucketReference{
		BucketName: "test-bucket",
		AccountID:  "test-account",
	}
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		nil,
		nil,
		nil,
		mockapproute,
		nil,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//create success
	{
		mockapproute.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		err := svc.createAPPRoute(context.TODO(), namer, "testvpc", "testappbox", sharedBucketRef, &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
	}
	//AlreadyExists
	{
		mockapproute.EXPECT().Create(gomock.Any(), gomock.Any()).Return(apierrors.NewAlreadyExists(apischema.GroupResource{}, ""))
		err := svc.createAPPRoute(context.TODO(), namer, "testvpc", "testappbox", sharedBucketRef, &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
	}
}

func TestCreateAPPBox(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockappbox := mockappbox.NewMockAPPBoxReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		nil,
		mockappbox,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//create success
	{
		mockappbox.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		err := svc.createAPPBox(context.TODO(), "testappbox", "testvpc", namer, &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
	}
	//AlreadyExists
	{
		mockappbox.EXPECT().Create(gomock.Any(), gomock.Any()).Return(apierrors.NewAlreadyExists(apischema.GroupResource{}, ""))
		err := svc.createAPPBox(context.TODO(), "testappbox", "testvpc", namer, &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
	}
}

func TestEnsureAPPBox(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockappbox := mockappbox.NewMockAPPBoxReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		nil,
		mockappbox,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//choose most large appbox
	{
		mockappbox.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.APPBoxList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.APPBox{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testappbox-0",
				},
				Status: v1alpha1.APPBoxStatus{
					Phase: v1alpha1.APPBoxPhaseActive,
				},
			}, v1alpha1.APPBox{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testappbox-1",
				},
				Status: v1alpha1.APPBoxStatus{
					Phase: v1alpha1.APPBoxPhaseActive,
				},
			})
			return nil
		}).Return(nil)
		boxname, err := svc.ensureAPPBox(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testappbox-1", boxname)
	}
	//modify appbox when ready for delete
	{
		mockappbox.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.APPBoxList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.APPBox{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testappbox-0",
				},
				Status: v1alpha1.APPBoxStatus{
					Phase: v1alpha1.APPBoxPhaseReadyForDelete,
				},
			})
			return nil
		}).Return(nil)
		mockappbox.EXPECT().UpdateStatus(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, appBox *v1alpha1.APPBox) error {
			assert.Equal(t, v1alpha1.APPBoxPhaseActive, appBox.Status.Phase)
			return nil
		}).Return(nil)
		boxname, err := svc.ensureAPPBox(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testappbox-0", boxname)
	}
	//create new appbox and increase name when failed or deleting
	{
		mockappbox.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.APPBoxList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.APPBox{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testappbox-0",
				},
				Status: v1alpha1.APPBoxStatus{
					Phase: v1alpha1.APPBoxPhaseDeleting,
				},
			})
			return nil
		}).Return(nil)
		mockappbox.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		boxname, err := svc.ensureAPPBox(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testappbox-1", boxname)
	}
	//create new appbox when not found
	{
		mockappbox.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.APPBoxList, opts ...client.ListOption) error {
			return nil
		}).Return(nil)
		mockappbox.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		boxname, err := svc.ensureAPPBox(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testvpc-0", boxname)
	}
}

func TestCreateInternet(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockinternet := mockinternet.NewMockInternetReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		mockinternet,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//create success
	{
		mockinternet.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		err := svc.createInternet(context.TODO(), "testinternet", "testvpc", namer, &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
	}
	//AlreadyExists
	{
		mockinternet.EXPECT().Create(gomock.Any(), gomock.Any()).Return(apierrors.NewAlreadyExists(apischema.GroupResource{}, ""))
		err := svc.createInternet(context.TODO(), "testinternet", "testvpc", namer, &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)

	}
}

func TestEnsureInternet(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockinternet := mockinternet.NewMockInternetReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		mockinternet,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//choose most large internet
	{
		mockinternet.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.InternetList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testinternet-0",
				},
				Status: v1alpha1.InternetStatus{
					Phase: v1alpha1.InternetPhaseActive,
				},
			}, v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testinternet-1",
				},
				Status: v1alpha1.InternetStatus{
					Phase: v1alpha1.InternetPhaseActive,
				},
			})
			return nil
		}).Return(nil)
		internetname, err := svc.ensureInternet(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testinternet-1", internetname)
	}
	//modify internet when ready for delete
	{
		mockinternet.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.InternetList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testinternet-0",
				},
				Status: v1alpha1.InternetStatus{
					Phase: v1alpha1.InternetPhaseReadyForDelete,
				},
			})
			return nil
		}).Return(nil)
		mockinternet.EXPECT().UpdateStatus(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, internet *v1alpha1.Internet) error {
			assert.Equal(t, v1alpha1.InternetPhaseActive, internet.Status.Phase)
			return nil
		}).Return(nil)
		internetname, err := svc.ensureInternet(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testinternet-0", internetname)
	}
	//create new internet and increase name when failed or deleting
	{
		mockinternet.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.InternetList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testinternet-0",
				},
				Status: v1alpha1.InternetStatus{
					Phase: v1alpha1.InternetPhaseDeleting,
				},
			})
			return nil
		}).Return(nil)
		mockinternet.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		internetname, err := svc.ensureInternet(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testinternet-1", internetname)
	}
	//create new internet when not found
	{
		mockinternet.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.InternetList, opts ...client.ListOption) error {
			return nil
		}).Return(nil)
		mockinternet.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		internetname, err := svc.ensureInternet(context.TODO(), namer, "testvpc", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		})
		assert.NoError(t, err)
		assert.Equal(t, "testvpc-0", internetname)
	}
}

func TestCreateVPC(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockvpc := mockvpc.NewMockVPCReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env: "ft",
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		mockvpc,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//create success
	{
		mockvpc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		account := v1alpha1.AccountReference{
			AccountID: "testaccount",
		}
		err := svc.createVPC(context.TODO(), "testvpc", "10.0.0.1/12", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		}, namer, &account)
		assert.NoError(t, err)
	}
	//AlreadyExists
	{
		mockvpc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(apierrors.NewAlreadyExists(apischema.GroupResource{}, ""))
		account := v1alpha1.AccountReference{
			AccountID: "testaccount",
		}
		err := svc.createVPC(context.TODO(), "testvpc", "10.0.0.1/12", &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
			},
		}, namer, &account)
		assert.NoError(t, err)
	}
}

func TestEnsureVPC(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockvpc := mockvpc.NewMockVPCReaderWriter(ctrl)
	mockaccount := mockAccountSvc.NewMockServiceInterface(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env:      "ft",
			Provider: string(constants.AWS),
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		mockvpc,
		nil,
		nil,
		nil,
		nil,
		nil,
		mockaccount,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//create when vpc id set, but not found
	{
		notfound := apierrors.NewNotFound(apischema.GroupResource{}, "")
		mockvpc.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(notfound)
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_ExistingAwsVpcId{
						ExistingAwsVpcId: "testvpc-aws",
					},
				},
			},
		}
		_, _, err := svc.ensureVPC(context.TODO(), namer, clusterreq)
		assert.ErrorIs(t, err, notfound)

	}
	//create when vpc id set, when ready for delete
	{
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_ExistingAwsVpcId{
						ExistingAwsVpcId: "testvpc-aws",
					},
				},
			},
		}
		mockvpc.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.VPC{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testvpc-aws",
				},
				Spec: v1alpha1.VPCSpec{
					AccountRef: &v1alpha1.AccountReference{
						AccountID: "testaccount",
					},
				},
				Status: v1alpha1.VPCStatus{
					Phase: v1alpha1.VPCPhaseReadyForDelete,
				},
			})
			return nil
		}).Return(nil)
		mockvpc.EXPECT().UpdateStatus(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, vpc *v1alpha1.VPC) error {
			assert.Equal(t, v1alpha1.VPCPhaseActive, vpc.Status.Phase)
			return nil
		}).Return(nil)
		vpcname, accountRef, err := svc.ensureVPC(context.TODO(), namer, clusterreq)
		assert.NoError(t, err)
		assert.Equal(t, "testvpc-aws", vpcname)
		assert.Equal(t, "testaccount", accountRef.AccountID)
	}
	//create when vpc id set, when deleting
	{
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_ExistingAwsVpcId{
						ExistingAwsVpcId: "testvpc-aws",
					},
				},
			},
		}
		mockvpc.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.VPC{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testvpc-aws",
				},
				Spec: v1alpha1.VPCSpec{
					AccountRef: &v1alpha1.AccountReference{
						AccountID: "testaccount",
					},
				},
				Status: v1alpha1.VPCStatus{
					Phase: v1alpha1.VPCPhaseDeleting,
				},
			})
			return nil
		}).Return(nil)
		_, _, err := svc.ensureVPC(context.TODO(), namer, clusterreq)
		assert.ErrorContains(t, err, "vpc status is terminating or not ready, can not create cluster")
	}
	//create when vpc id set, when active
	{
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_ExistingAwsVpcId{
						ExistingAwsVpcId: "testvpc-aws",
					},
				},
			},
		}
		mockvpc.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.VPC{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testvpc-aws",
				},
				Spec: v1alpha1.VPCSpec{
					AccountRef: &v1alpha1.AccountReference{
						AccountID: "testaccount",
					},
				},
				Status: v1alpha1.VPCStatus{
					Phase: v1alpha1.VPCPhaseActive,
				},
			})
			return nil
		}).Return(nil)
		vpcname, accountRef, err := svc.ensureVPC(context.TODO(), namer, clusterreq)
		assert.NoError(t, err)
		assert.Equal(t, "testvpc-aws", vpcname)
		assert.Equal(t, "testaccount", accountRef.AccountID)
	}
	//create when vpc id not set, create with vpc cidr aws
	{
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_NewCidrBlock{
						NewCidrBlock: "10.0.0.1/12",
					},
				},
			},
		}
		mockaccount.EXPECT().AssignAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "testaccount-new",
				AWSRole:    "testrole",
			},
		}, "testprodiver", nil)
		mockvpc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		vpcname, accountRef, err := svc.ensureVPC(context.TODO(), namer, clusterreq)
		assert.NoError(t, err)
		assert.Equal(t, namer.GetUniqueName(), vpcname)
		assert.Equal(t, "testaccount-new", accountRef.AccountID)
	}
	//create when vpc id not set, create with vpc cidr gcp
	{
		//set provider to gcp
		cfg.Provider = string(constants.GCP)
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_NewCidrBlock{
						NewCidrBlock: "10.0.0.1/12",
					},
				},
			},
		}
		mockaccount.EXPECT().AssignAccount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(models.DataPlaneAccountInfo{
			GCPAccountInfo: &models.GCPAccountInfo{
				ProjectID: "testproject-new",
			},
		}, "testprodiver", nil)
		mockvpc.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		vpcname, accountRef, err := svc.ensureVPC(context.TODO(), namer, clusterreq)
		assert.NoError(t, err)
		assert.Equal(t, namer.GetUniqueName(), vpcname)
		assert.Equal(t, "testproject-new", accountRef.AccountID)
	}
}
func TestEnsureNamespace(t *testing.T) {
	ctrl := gomock.NewController(t)
	mocknamespace := mocknamespace.NewMockNamespaceReaderWriter(ctrl)
	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env:      "ft",
			Provider: string(constants.AWS),
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		nil,
		nil,
		nil,
		nil,
		nil,
		mocknamespace,
		nil,
		nil,
		nil,
		cfg,
		tmplcfg,
	)
	namer := utils.NewNamerWithClusterID("test", cfg.RegionConfig.Name, cfg.Env)
	//create when not found
	{
		mocknamespace.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(apischema.GroupResource{}, ""))
		mocknamespace.EXPECT().Create(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, ns *corev1.Namespace) error {
			assert.Equal(t, namer.GetNamespace(), ns.Name)
			return nil
		}).Return(nil)
		err := svc.ensureNamespace(context.TODO(), namer)
		assert.NoError(t, err)
	}
	//create but already exists
	{
		mocknamespace.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(apischema.GroupResource{}, ""))
		mocknamespace.EXPECT().Create(gomock.Any(), gomock.Any()).Return(apierrors.NewAlreadyExists(apischema.GroupResource{}, ""))
		err := svc.ensureNamespace(context.TODO(), namer)
		assert.NoError(t, err)
	}
	//already exists
	{
		mocknamespace.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
		err := svc.ensureNamespace(context.TODO(), namer)
		assert.NoError(t, err)
	}
}

func TestCreate(t *testing.T) {
	ctrl := gomock.NewController(t)
	mockvpc := mockvpc.NewMockVPCReaderWriter(ctrl)
	mockinternet := mockinternet.NewMockInternetReaderWriter(ctrl)
	mockappbox := mockappbox.NewMockAPPBoxReaderWriter(ctrl)
	mockapproute := mockapproute.NewMockAPPRouteReaderWriter(ctrl)
	mockappcontroller := mockappcontroller.NewMockAPPControllerReaderWriter(ctrl)
	mocknamespace := mocknamespace.NewMockNamespaceReaderWriter(ctrl)
	mockaccount := mockAccountSvc.NewMockServiceInterface(ctrl)
	mockAccountReader := mockAccountRsc.NewMockAccountReader(ctrl)
	mockBucketService := mockAccountSvc.NewMockSharedBucketServiceInterface(ctrl)

	cfg := rsconfig.RootConfig{
		ControllerConfig: rsconfig.ControllerConfig{
			RegionConfig: rsconfig.RegionConfig{
				Name: "test-region",
			},
			Env:      "ft",
			Provider: string(constants.AWS),
		},
		FeatureGates: map[config.FeatureGateName]config.FeatureGate{
			config.AllocateSharedBucket: {
				All: true,
			},
		},
	}
	tmplcfg := rsconfig.TemplatesConfig{}
	svc := NewClusterService(
		mockvpc,
		mockinternet,
		mockappbox,
		mockappcontroller,
		mockapproute,
		mocknamespace,
		mockaccount,
		mockBucketService,
		mockAccountReader,
		cfg,
		tmplcfg,
	)
	//create success
	{
		clusterreq := &regionalpb.CreateClusterRequest{
			Cluster: &regionalpb.Cluster{
				Id: "test",
				VpcSettings: &regionalpb.Cluster_VpcSettings{
					Vpc: &regionalpb.Cluster_VpcSettings_ExistingAwsVpcId{
						ExistingAwsVpcId: "testvpc-aws",
					},
				},
				ClusterVersion: &regionalpb.ClusterVersion{
					ClusterVersion: "1.0.0",
				},
			},
		}
		//ensure namespace
		mocknamespace.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
		//approute not found
		mockapproute.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(apischema.GroupResource{}, ""))
		//ensurevpc
		mockvpc.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.VPC{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testvpc-aws",
				},
				Spec: v1alpha1.VPCSpec{
					AccountRef: &v1alpha1.AccountReference{
						AccountID: "testaccount",
					},
				},
				Status: v1alpha1.VPCStatus{
					Phase: v1alpha1.VPCPhaseActive,
				},
			})
			return nil
		}).Return(nil)
		//ensure internet
		mockinternet.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.InternetList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.Internet{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testinternet-0",
				},
				Status: v1alpha1.InternetStatus{
					Phase: v1alpha1.InternetPhaseActive,
				},
			})
			return nil
		}).Return(nil)
		//ensure appbox
		mockappbox.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, list *v1alpha1.APPBoxList, opts ...client.ListOption) error {
			list.Items = append(list.Items, v1alpha1.APPBox{
				ObjectMeta: metav1.ObjectMeta{
					Name: "testappbox-0",
				},
				Status: v1alpha1.APPBoxStatus{
					Phase: v1alpha1.APPBoxPhaseActive,
				},
			})
			return nil
		}).Return(nil)
		// assign shared bucket
		mockAccountReader.EXPECT().GetByRef(gomock.Any(), gomock.Any()).Return(models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "testaccount",
			},
		}, nil)
		mockBucketService.EXPECT().AssignSharedBucket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(models.SharedBucketInfo{
			BucketName: "testbucket",
		}, nil)
		//create approute
		mockapproute.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
		cluster, err := svc.Create(context.Background(), clusterreq)
		assert.NoError(t, err)
		assert.Equal(t, "test", cluster.Id)
	}
	//return if approute exists
}
