package service

import (
	"context"
	"encoding/json"
	"testing"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"github.com/stretchr/testify/assert"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-agent/v1"
	apicommon "github.com/tidbcloud/infra-provider/api/tidbcloud/common"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/pingcap/label"
)

func Test_convertTC(t *testing.T) {
	tc := buildTestTC("test", testNS, true, nil)
	tc.Annotations[cloudv1.ConfigRevisionIdKey] = "config-revision-id"
	tc.Spec.TiDB = &v1alpha1.TiDBSpec{
		ComponentSpec: v1alpha1.ComponentSpec{
			Annotations: map[string]string{
				consts.TcInstanceTypeAnnotationKey: "m5.xlarge",
			},
		},
		Replicas: 3,
	}
	type args struct {
		tc *v1alpha1.TidbCluster
	}
	tests := []struct {
		name string
		args args
		want *apiv1.TC
	}{
		{
			name: "normal case",
			args: args{
				tc: tc,
			},
			want: &apiv1.TC{
				K8SMetadata: &apiv1.K8SMetadata{
					Name:        tc.Name,
					Namespace:   tc.Namespace,
					Labels:      tc.Labels,
					Annotations: tc.Annotations,
				},
				Paused:           false,
				ConfigRevisionId: "config-revision-id",
				ComponentSpecs: []*apiv1.TC_ComponentSpec{
					{
						ComponentType:        apicommon.ComponentType_TIDB,
						ProviderInstanceType: "m5.xlarge",
						Replicas:             3,
					},
				},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equalf(t, tt.want, convertTC(tt.args.tc), "convertTC(%v)", tt.args.tc)
		})
	}
}

func TestAgentService_DeleteTCMember(t *testing.T) {
	type args struct {
		tcName     string
		memberName string
		tcModifier func(tc *v1alpha1.TidbCluster)
	}
	podName := "db-tidb-1"
	tests := []struct {
		name                  string
		args                  args
		wantErr               bool
		expectedSlotAnnoValue string
		expectedNewMemberName string
	}{
		{
			name: "pod not found",
			args: args{
				tcName:     testTcName,
				memberName: "not found",
			},
			wantErr:               true,
			expectedSlotAnnoValue: "",
			expectedNewMemberName: "",
		},
		{
			name: "slot annotation doesnt exist and add slot OK",
			args: args{
				tcName:     testTcName,
				memberName: podName,
			},
			wantErr:               false,
			expectedSlotAnnoValue: "[1]",
			expectedNewMemberName: "db-tidb-3",
		}, {
			name: "this func should be idempotent",
			args: args{
				tcName:     testTcName,
				memberName: podName,
				tcModifier: func(tc *v1alpha1.TidbCluster) {
					tc.Annotations[label.AnnTiDBDeleteSlots] = "[1]"
				},
			},
			wantErr:               false,
			expectedSlotAnnoValue: "[1]",
			expectedNewMemberName: "db-tidb-3",
		}, {
			name: "slot annotation non empty and append slot OK",
			args: args{
				tcName:     testTcName,
				memberName: podName,
				tcModifier: func(tc *v1alpha1.TidbCluster) {
					tc.Annotations[label.AnnTiDBDeleteSlots] = "[2,4]"
				},
			},
			wantErr:               false,
			expectedSlotAnnoValue: "[2,4,1]",
			expectedNewMemberName: "db-tidb-5",
		},
		{
			name: "slot annotation non empty and append slot OK",
			args: args{
				tcName:     testTcName,
				memberName: podName,
				tcModifier: func(tc *v1alpha1.TidbCluster) {
					tc.Annotations[label.AnnTiDBDeleteSlots] = "[3,5]"
				},
			},
			wantErr:               false,
			expectedSlotAnnoValue: "[3,5,1]",
			expectedNewMemberName: "db-tidb-4",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := context.Background()
			req := &apiv1.DeleteTCMemberReq{
				ClusterId:  testClusterId,
				MemberName: tt.args.memberName,
			}
			tc := buildTestTC(testTcName, testNS, true, nil)
			tc.Spec.TiDB = &v1alpha1.TiDBSpec{
				Replicas: 3,
			}
			if tt.args.tcModifier != nil {
				tt.args.tcModifier(tc)
			}
			pod := buildPod(podName, testNS, "node1", string(v1alpha1.TiDBMemberType))
			pod.GenerateName = "db-tidb-"
			cli := newFakeClient([]client.Object{tc, pod})
			s := &AgentService{
				dataplaneCli: cli,
			}

			_, err := s.DeleteTCMember(ctx, req)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}

			var actualTc v1alpha1.TidbCluster
			assert.NoErrorf(t, cli.Get(ctx, client.ObjectKeyFromObject(tc), &actualTc), "get actual tc")
			annoValue := actualTc.Annotations[label.AnnTiDBDeleteSlots]
			if annoValue != "" {
				assert.NoError(t, json.Unmarshal([]byte(annoValue), &[]int32{}))
			}
			assert.Equalf(t, tt.expectedSlotAnnoValue, annoValue, "expected to add slot")
		})
	}
}
