package workflow

import (
	"testing"

	"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"
)

func TestPb2Entity(t *testing.T) {
	type args struct {
		action *chaos.ExperimentAction
	}
	tests := []struct {
		name    string
		args    args
		want    *entity.WorkflowSpec
		wantErr bool
	}{
		{
			name: "should be ok",
			args: args{
				action: &chaos.ExperimentAction{
					Type: chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN,
					PodShutdown: &chaos.ExperimentAction_ActionPodShutdown{
						Target: &chaos.ExperimentTarget{
							Mode:      chaos.TargetMode_TARGET_MODE_RANDOM,
							Component: chaos.TargetComponent_TARGET_COMPONENT_TIDB,
						},
					},
				},
			},
			want: &entity.WorkflowSpec{
				EntryNodeName: entryNodeName,
				Templates: map[string]entity.WorkflowNodeTemplate{
					entryNodeName: {
						Name:     entryNodeName,
						Type:     constant.WorkflowTemplateTypeSerial,
						Children: []string{actionNodeName},
					},
					actionNodeName: {
						Name: actionNodeName,
						Type: constant.WorkflowTemplateTypePodShutdown,
						EmbedAction: &entity.EmbedAction{
							PodShutdown: &entity.PodShutdownAction{
								Target: chaos.ExperimentTarget{
									Mode:      chaos.TargetMode_TARGET_MODE_RANDOM,
									Component: chaos.TargetComponent_TARGET_COMPONENT_TIDB,
								},
							},
						},
					},
				},
			},
			wantErr: false,
		},
		{
			name: "invalid input, empty action",
			args: args{
				action: nil,
			},
			want:    nil,
			wantErr: true,
		},
		{
			name: "invalid input, empty target",
			args: args{
				action: &chaos.ExperimentAction{
					Type: chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN,
					PodShutdown: &chaos.ExperimentAction_ActionPodShutdown{
						Target: nil,
					},
				},
			},
			want:    nil,
			wantErr: true,
		},
		{
			name: "invalid input, action type not supported",
			args: args{
				action: &chaos.ExperimentAction{
					Type: chaos.ActionKind_ACTION_KIND_UNSPECIFIED,
				},
			},
			want:    nil,
			wantErr: true,
		},
		{
			name: "invalid input, target mode not supported",
			args: args{
				action: &chaos.ExperimentAction{
					Type: chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN,
					PodShutdown: &chaos.ExperimentAction_ActionPodShutdown{
						Target: &chaos.ExperimentTarget{
							Mode: chaos.TargetMode_TARGET_MODE_UNSPECIFIED,
						},
					},
				},
			},
			want:    nil,
			wantErr: true,
		},
		{
			name: "invalid input, target name not specified when target mode is specified",
			args: args{
				action: &chaos.ExperimentAction{
					Type: chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN,
					PodShutdown: &chaos.ExperimentAction_ActionPodShutdown{
						Target: &chaos.ExperimentTarget{
							Mode:      chaos.TargetMode_TARGET_MODE_SPECIFIED,
							Component: chaos.TargetComponent_TARGET_COMPONENT_TIDB,
						},
					},
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			svc := &specConverter{}
			got, err := svc.Pb2Entity(tt.args.action)
			if (err != nil) != tt.wantErr {
				t.Errorf("Pb2Entity() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			assert.Equal(t, got, tt.want, "Pb2Entity() got = %v, want %v", got, tt.want)
		})
	}
}

func TestEntity2Pb(t *testing.T) {
	type args struct {
		spec entity.WorkflowSpec
	}
	tests := []struct {
		name    string
		args    args
		want    *chaos.ExperimentAction
		wantErr bool
	}{
		{
			name: "should be ok",
			args: args{
				spec: entity.WorkflowSpec{
					EntryNodeName: entryNodeName,
					Templates: map[string]entity.WorkflowNodeTemplate{
						entryNodeName: {
							Name:     entryNodeName,
							Type:     constant.WorkflowTemplateTypeSerial,
							Children: []string{actionNodeName},
						},
						actionNodeName: {
							Name: actionNodeName,
							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: &chaos.ExperimentAction{
				Type: chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN,
				PodShutdown: &chaos.ExperimentAction_ActionPodShutdown{
					Target: &chaos.ExperimentTarget{
						Mode:      chaos.TargetMode_TARGET_MODE_RANDOM,
						Component: chaos.TargetComponent_TARGET_COMPONENT_TIDB,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "invalid input, empty action",
			args: args{
				spec: entity.WorkflowSpec{
					EntryNodeName: entryNodeName,
					Templates: map[string]entity.WorkflowNodeTemplate{
						entryNodeName: {
							Name:     entryNodeName,
							Type:     constant.WorkflowTemplateTypeSerial,
							Children: []string{actionNodeName},
						},
						actionNodeName: {
							Name: actionNodeName,
							Type: constant.WorkflowTemplateTypePodShutdown,
							EmbedAction: &entity.EmbedAction{
								PodShutdown: nil,
							},
						},
					},
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			svc := &specConverter{}
			got, err := svc.Entity2Pb(tt.args.spec)
			if (err != nil) != tt.wantErr {
				t.Errorf("Entity2Pb() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			assert.Equal(t, got, tt.want, "Entity2Pb() got = %v, want %v", got, tt.want)
		})
	}
}
