package loadbalancer

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	regionalapiv1 "github.com/tidbcloud/cluster-service-ng/api/tidbcloud/dataplane-manager/v1"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	bizerr "github.com/tidbcloud/infra-provider/regional-server/pkg/common/errors"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/repository"
	dpmscheme "github.com/tidbcloud/infra-provider/regional-server/pkg/scheme"
	"google.golang.org/protobuf/proto"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestLoadBalancerOwnership(t *testing.T) {
	ctx := context.Background()
	dataPlane := &v1alpha1.DataPlane{ObjectMeta: metav1.ObjectMeta{Name: "dp-1", Namespace: "default"}}
	cli := fake.NewClientBuilder().WithScheme(dpmscheme.Scheme).WithObjects(dataPlane).Build()
	service := NewLoadBalancerService(
		repository.NewLoadBalancerReaderWriter(cli),
		repository.NewDataPlaneReader(cli),
		config.RootConfig{Namespace: "default"},
	)
	request := &regionalapiv1.CreateLoadBalancerRequest{
		DataPlaneId: "dp-1",
		Id:          "lb-1",
		Loadbalancer: &regionalapiv1.LoadBalancer{
			Identity: "cluster-1",
			Port:     4000,
			Tags:     map[string]string{consts.TenantLabelKey: "tenant-1"},
		},
		OwnershipContext: &regionalapiv1.ResourceOwnershipContext{SharedPoolId: "sp-1"},
	}

	_, err := service.CreateLoadBalancer(ctx, request)
	require.NoError(t, err)

	created := &v1alpha1.LoadBalancer{}
	require.NoError(t, cli.Get(ctx, types.NamespacedName{Namespace: "default", Name: "lb-1"}, created))
	require.Equal(t, &v1alpha1.ResourceOwnership{SharedPoolID: "sp-1"}, created.Spec.Ownership)

	_, err = service.CreateLoadBalancer(ctx, proto.Clone(request).(*regionalapiv1.CreateLoadBalancerRequest))
	require.True(t, bizerr.Is(err, bizerr.ResourceConflict), "AlreadyExists behavior must be preserved")

	changed := proto.Clone(request).(*regionalapiv1.CreateLoadBalancerRequest)
	changed.Loadbalancer.Identity = "cluster-2"
	_, err = service.CreateLoadBalancer(ctx, changed)
	require.True(t, bizerr.Is(err, bizerr.ResourceConflict), "create intent is not compared")

	_, err = service.DeleteLoadBalancer(ctx, &regionalapiv1.DeleteLoadBalancerRequest{DataPlaneId: "dp-1", Id: "lb-1"})
	require.True(t, bizerr.Is(err, bizerr.ResourceConflict))
	require.NoError(t, cli.Get(ctx, types.NamespacedName{Namespace: "default", Name: "lb-1"}, created))

	_, err = service.DeleteLoadBalancer(ctx, &regionalapiv1.DeleteLoadBalancerRequest{
		DataPlaneId:      "dp-1",
		Id:               "lb-1",
		OwnershipContext: &regionalapiv1.ResourceOwnershipContext{SharedPoolId: "sp-1"},
	})
	require.NoError(t, err)
}

func TestOwnerlessLoadBalancerRemainsCompatible(t *testing.T) {
	ctx := context.Background()
	loadBalancer := &v1alpha1.LoadBalancer{
		ObjectMeta: metav1.ObjectMeta{Name: "lb-1", Namespace: "default"},
		Spec:       v1alpha1.LoadBalancerSpec{DataPlane: v1alpha1.Reference{Name: "dp-1", Namespace: "default"}},
	}
	cli := fake.NewClientBuilder().WithScheme(dpmscheme.Scheme).WithObjects(loadBalancer).Build()
	service := NewLoadBalancerService(
		repository.NewLoadBalancerReaderWriter(cli),
		repository.NewDataPlaneReader(cli),
		config.RootConfig{Namespace: "default"},
	)

	_, err := service.GetLoadBalancer(ctx, &regionalapiv1.GetLoadBalancerRequest{
		DataPlaneId:      "dp-1",
		Id:               "lb-1",
		OwnershipContext: &regionalapiv1.ResourceOwnershipContext{SharedPoolId: "sp-1"},
	})
	require.NoError(t, err)
}
