package privatelink

import (
	"strconv"
	"testing"

	. "github.com/onsi/gomega"
	regionaldedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/pkgv2/db/mock_db"
	"go.uber.org/mock/gomock"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver/mock_regionserver"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	clusterrepomock "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cluster/mock_cluster"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/endpoint"
	endpointrepomock "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/endpoint/mock_endpoint"
	regionrepomock "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/region/mock_region"
	tidbgrouprepomock "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/tidbgroup/mock_tidbgroup"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/idgenerator"
	workflowmock "github.com/tidbcloud/tidb-management-service/utilities/workflow/mock_workflow"
)

func TestCreatePrivateLinkEndpoint(t *testing.T) {
	g := NewWithT(t)
	// Init mock dependencies.
	ctrl := gomock.NewController(t)
	regionalCli := mock_regionserver.NewMockClient(ctrl)
	fakeDB := mock_db.NewFakeTransProvider()
	clusterrepo := clusterrepomock.NewMockRepo(ctrl)
	privatelinkendpointrepo := endpointrepomock.NewMockPrivateLinkEndpointRepo(ctrl)
	tidbgrouprepo := tidbgrouprepomock.NewMockRepo(ctrl)
	regionrepo := regionrepomock.NewMockRepo(ctrl)
	wf := workflowmock.NewMockWorkflow(ctrl)
	svc := NewService(
		&config.Config{},
		clusterrepo,
		privatelinkendpointrepo,
		tidbgrouprepo,
		regionrepo,
		regionalCli,
		wf,
		idgenerator.NewSonyflakeID(idgenerator.NodeID(1), ""),
		fakeDB,
	)

	req := &globaldedicatedv1.CreatePrivateLinkEndpointRequest{
		OrgId:       123,
		ProjectId:   456,
		TidbGroupId: 789,
		ClusterId:   123456,
		PrivateLinkEndpoint: &globaldedicatedv1.PrivateLinkEndpoint{
			EndpointName:     "/subscriptions/sub-id/resourceGroups/rsg-id/providers/Microsoft.Network/privateEndpoints/endpint-name",
			Type:             globaldedicatedv1.PrivateLinkEndpoint_TYPE_CUSTOMER,
			PrivateIpAddress: "10.3.4.5",
		},
	}
	cloudProviderId := globalcommonv1.CloudProvider_AZURE
	regionId := globalcommonv1.Region_AZURE_ASIA
	region := &entity.Region{
		Id: regionId,
	}
	connection, err := buildPrivateLinkConnection(req, cloudProviderId)
	g.Expect(err).Should(BeNil())
	ctx, span := tracer.Start(t.Context(), "Domain.CreatePrivateLinkEndpointSvc")
	defer span.End()

	t.Run("validate only for azure", func(t *testing.T) {
		req.ValidateOnly = true
		tidbgrouprepo.EXPECT().Load(ctx, req.ClusterId, req.TidbGroupId).Return(nil, nil).Times(3)
		clusterrepo.EXPECT().Get(ctx, req.ClusterId, false).Return(&entity.Cluster{
			Cluster: &globaldedicatedv1.Cluster{
				CloudProviderId: cloudProviderId,
				ClusterId:       req.ClusterId,
				RegionId:        regionId,
				OrganizationId:  req.OrgId,
				State:           globaldedicatedv1.Cluster_AVAILABLE,
			},
		}, nil).Times(3)
		regionrepo.EXPECT().SelectRegionById(ctx, regionId, req.OrgId).Return(region, nil).Times(3)
		regionalCli.EXPECT().GetPrivateLinkService(ctx, region, &regionserver.GetPrivateLinkServiceRequest{
			ClusterId:   strconv.FormatUint(req.ClusterId, 10),
			TidbGroupId: strconv.FormatUint(req.TidbGroupId, 10),
		}).Return(&regionaldedicatedv1.PrivateLinkService{
			Status: regionaldedicatedv1.PrivateLinkService_ACTIVE,
		}, nil).Times(3)
		privatelinkendpointrepo.EXPECT().Query(ctx, &endpoint.PrivateLinkEndpointFilter{
			ClusterIDs: []uint64{req.ClusterId},
		}).Return(nil, nil).Times(2)

		t.Run("validate success", func(t *testing.T) {
			regionalCli.EXPECT().
				UpdatePrivateLinkService(ctx, region, &regionserver.UpdatePrivateLinkServiceRequest{
					ClusterId:   strconv.FormatUint(req.ClusterId, 10),
					TidbGroupId: strconv.FormatUint(req.TidbGroupId, 10),
					PrivateLinkConnections: []*regionaldedicatedv1.PrivateLinkService_Connection{
						connection,
					},
					ValidateOnly: true,
				}).
				Return(nil, nil)
			_, err = svc.CreatePrivateLinkEndpoint(t.Context(), req) //nolint:contextcheck // for test
			g.Expect(err).Should(BeNil())
		})

		t.Run("bad request", func(t *testing.T) {
			fakeErr := bizerr.New(bizerr.ErrCodeParams, "bad request")
			regionalCli.EXPECT().
				UpdatePrivateLinkService(ctx, region, &regionserver.UpdatePrivateLinkServiceRequest{
					ClusterId:   strconv.FormatUint(req.ClusterId, 10),
					TidbGroupId: strconv.FormatUint(req.TidbGroupId, 10),
					PrivateLinkConnections: []*regionaldedicatedv1.PrivateLinkService_Connection{
						connection,
					},
					ValidateOnly: true,
				}).
				Return(nil, fakeErr)
			_, err = svc.CreatePrivateLinkEndpoint(t.Context(), req) //nolint:contextcheck // for test
			g.Expect(err).Should(Equal(bizerr.WrapError(fakeErr, "create privatelink endpoint failed")))
		})
		t.Run("invalid ip address", func(t *testing.T) { //nolint:contextcheck // for test
			req.PrivateLinkEndpoint.PrivateIpAddress = "1.2.3"
			_, err = svc.CreatePrivateLinkEndpoint(t.Context(), req)
			g.Expect(err).ShouldNot(BeNil())
			g.Expect(err.Error()).Should(ContainSubstring("invalid private IP address: 1.2.3"))
		})
	})
}
