package facade_imp

import (
	"context"
	"fmt"
	"testing"

	workflowv1alpha1 "github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/chaos-svc/idl/pbgen/proto/chaos"
	"github.com/tidbcloud/chaos-svc/pkg/domain/constant"
	"github.com/tidbcloud/chaos-svc/pkg/domain/entity"
	"github.com/tidbcloud/chaos-svc/pkg/domain/facade"
	cloudv1alpha1 "github.com/tidbcloud/eks-provider/api/v1alpha1"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestEntity2CR(t *testing.T) {
	type fields struct {
	}
	type args struct {
		workflow *entity.Workflow
	}
	var (
		testProjectID       uint64 = 1111
		testProjectName            = "test"
		testClusterID       uint64 = 2222
		testTiDBPodName            = "tidb-0"
		testDeadline               = "10s"
		testNetworkName            = "us-west-2"
		testProviderType           = "eks"
		testNamespace              = fmt.Sprintf("%s-%d", testProjectName, testProjectID)
		testRemoteNamespace        = fmt.Sprintf("tidb%d", testClusterID)
		testClusterCR              = cloudv1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name:      fmt.Sprintf("%d", testClusterID),
				Namespace: fmt.Sprintf("%s-%d", testProjectName, testProjectID),
				Labels: map[string]string{
					facade.LabelKeyProviderType: testProviderType,
					facade.LabelKeyRegion:       testNetworkName,
				},
			},
			Spec: cloudv1.ClusterSpec{
				NetworkName: testNetworkName,
			},
		}
	)
	tests := []struct {
		name    string
		args    args
		want    *cloudv1alpha1.ChaosWorkflow
		wantErr bool
	}{
		{
			name: "pod_shutdown with random target",
			args: args{
				workflow: &entity.Workflow{
					ID:        1,
					ProjectID: testProjectID,
					ClusterID: testClusterID,
					Spec: entity.WorkflowSpec{
						EntryNodeName: "node1",
						Templates: map[string]entity.WorkflowNodeTemplate{
							"node1": {
								Name:     "node1",
								Type:     constant.WorkflowTemplateTypeSerial,
								Children: []string{"node2"},
							},
							"node2": {
								Name: "node2",
								Type: constant.WorkflowTemplateTypePodShutdown,
								EmbedAction: &entity.EmbedAction{
									PodShutdown: &entity.PodShutdownAction{
										Target: chaos.ExperimentTarget{
											Mode:      chaos.TargetMode_TARGET_MODE_RANDOM,
											Component: chaos.TargetComponent_TARGET_COMPONENT_TIDB,
										},
									},
								},
							},
						},
					},
				},
			},
			want: &cloudv1alpha1.ChaosWorkflow{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "1",
					Namespace: testNamespace,
					Labels: map[string]string{
						facade.LabelKeyProviderType: testProviderType,
						facade.LabelKeyRegion:       testNetworkName,
					},
				},
				Spec: cloudv1alpha1.ChaosWorkflowSpec{
					NetworkName:     testNetworkName,
					RemoteNamespace: testRemoteNamespace,
					Entry:           "node1",
					Templates: []workflowv1alpha1.Template{
						{
							Name:     "node1",
							Type:     workflowv1alpha1.TypeSerial,
							Children: []string{"node2"},
						},
						{
							Name:     "node2",
							Type:     workflowv1alpha1.TypePodChaos,
							Deadline: &testDeadline,
							EmbedChaos: &workflowv1alpha1.EmbedChaos{
								PodChaos: &workflowv1alpha1.PodChaosSpec{
									Action: workflowv1alpha1.PodKillAction,
									ContainerSelector: workflowv1alpha1.ContainerSelector{
										PodSelector: workflowv1alpha1.PodSelector{
											Mode: workflowv1alpha1.OneMode,
											Selector: workflowv1alpha1.PodSelectorSpec{
												GenericSelectorSpec: workflowv1alpha1.GenericSelectorSpec{
													Namespaces:     []string{testRemoteNamespace},
													LabelSelectors: map[string]string{labelKeyComponentType: "tidb"},
												},
											},
										},
									},
								},
							},
						},
					},
				},
			},
			wantErr: false,
		},
		{
			name: "pod_shutdown with specified target",
			args: args{
				workflow: &entity.Workflow{
					ID:        1,
					ProjectID: testProjectID,
					ClusterID: testClusterID,
					Spec: entity.WorkflowSpec{
						EntryNodeName: "node1",
						Templates: map[string]entity.WorkflowNodeTemplate{
							"node1": {
								Name:     "node1",
								Type:     constant.WorkflowTemplateTypeSerial,
								Children: []string{"node2"},
							},
							"node2": {
								Name: "node2",
								Type: constant.WorkflowTemplateTypePodShutdown,
								EmbedAction: &entity.EmbedAction{
									PodShutdown: &entity.PodShutdownAction{
										Target: chaos.ExperimentTarget{
											Mode:      chaos.TargetMode_TARGET_MODE_SPECIFIED,
											Component: chaos.TargetComponent_TARGET_COMPONENT_TIDB,
											Name:      &testTiDBPodName,
										},
									},
								},
							},
						},
					},
				},
			},
			want: &cloudv1alpha1.ChaosWorkflow{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "1",
					Namespace: testNamespace,
					Labels: map[string]string{
						facade.LabelKeyProviderType: testProviderType,
						facade.LabelKeyRegion:       testNetworkName,
					},
				},
				Spec: cloudv1alpha1.ChaosWorkflowSpec{
					NetworkName:     testNetworkName,
					RemoteNamespace: testRemoteNamespace,
					Entry:           "node1",
					Templates: []workflowv1alpha1.Template{
						{
							Name:     "node1",
							Type:     workflowv1alpha1.TypeSerial,
							Children: []string{"node2"},
						},
						{
							Name:     "node2",
							Type:     workflowv1alpha1.TypePodChaos,
							Deadline: &testDeadline,
							EmbedChaos: &workflowv1alpha1.EmbedChaos{
								PodChaos: &workflowv1alpha1.PodChaosSpec{
									Action: workflowv1alpha1.PodKillAction,
									ContainerSelector: workflowv1alpha1.ContainerSelector{
										PodSelector: workflowv1alpha1.PodSelector{
											Mode: workflowv1alpha1.OneMode,
											Selector: workflowv1alpha1.PodSelectorSpec{
												GenericSelectorSpec: workflowv1alpha1.GenericSelectorSpec{
													Namespaces: []string{testRemoteNamespace},
												},
												Pods: map[string][]string{
													testRemoteNamespace: {testTiDBPodName},
												},
											},
										},
									},
								},
							},
						},
					},
				},
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			scheme := runtime.NewScheme()
			err := cloudv1.AddToScheme(scheme)
			if err != nil {
				t.Errorf("register infra API cloudv1: %s", err)
				return
			}

			client := fakeclient.NewClientBuilder().
				WithScheme(scheme).
				WithObjects(&testClusterCR).
				Build()
			infra := &infraImp{
				projectName: testProjectName,
				client:      client,
			}
			got, err := infra.entity2CR(context.Background(), tt.args.workflow)
			if (err != nil) != tt.wantErr {
				t.Errorf("Entity2CR() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			assert.ElementsMatch(t, got.Spec.Templates, tt.want.Spec.Templates, "Entity2CR() got = %v, want %v", got.Spec.Templates, tt.want.Spec.Templates)
			got.Spec.Templates = nil
			tt.want.Spec.Templates = nil
			assert.Equal(t, got, tt.want, "Entity2CR() got = %v, want %v", got, tt.want)
		})
	}
}
