package entity

import (
	"testing"
	"time"

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

func TestStartEnable(t *testing.T) {
	type fields struct {
		status         chaos.ChaosBackendStatus
		type2Operation map[constant.ChaosBackendOperationType]*vo.BackendOperation
	}
	type args struct {
		userId uint64
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *Backend
		wantErr bool
	}{
		{
			name: "should be ok",
			fields: fields{
				status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			args: args{
				userId: 1,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALLING,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeEnable,
						Status:      constant.ChaosBackendOperationStatusStart,
						CreatedAt:   time.Time{},
						CreatedByID: 1,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be failed, because the status is uninstalling",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
			},
			args: args{
				userId: 1,
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			backend := &Backend{
				Status:         tt.fields.status,
				Type2Operation: tt.fields.type2Operation,
			}
			err := backend.StartEnable(tt.args.userId)
			if (err != nil) != tt.wantErr {
				t.Errorf("StartEnable() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr {
				assert.Equal(t, backend, tt.want, "StartEnable() got = %v, want %v", backend, tt.want)
			}
		})
	}
}

func TestStartDisable(t *testing.T) {
	type fields struct {
		status         chaos.ChaosBackendStatus
		type2Operation map[constant.ChaosBackendOperationType]*vo.BackendOperation
	}
	type args struct {
		userId uint64
		now    time.Time
	}
	testTime := time.Now()
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *Backend
		wantErr bool
	}{
		{
			name: "should be ok, when the backend is ready",
			fields: fields{
				status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_READY,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			args: args{
				userId: 1,
				now:    testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeDisable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeDisable,
						Status:      constant.ChaosBackendOperationStatusStart,
						CreatedAt:   time.Time{},
						CreatedByID: 1,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be ok, when the backend is install_failed",
			fields: fields{
				status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALL_FAILED,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			args: args{
				userId: 1,
				now:    testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeDisable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeDisable,
						Status:      constant.ChaosBackendOperationStatusStart,
						CreatedAt:   time.Time{},
						CreatedByID: 1,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be ok, when the backend is installing",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALLING,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						Status: constant.ChaosBackendOperationStatusStart,
					},
				},
			},
			args: args{
				userId: 1,
				now:    testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						Status:      constant.ChaosBackendOperationStatusDone,
						CompletedAt: &testTime,
					},
					constant.ChaosBackendOperationTypeDisable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeDisable,
						Status:      constant.ChaosBackendOperationStatusStart,
						CreatedAt:   time.Time{},
						CreatedByID: 1,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be failed, because the status is uninstalling",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
			},
			args: args{
				userId: 1,
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			backend := &Backend{
				Status:         tt.fields.status,
				Type2Operation: tt.fields.type2Operation,
			}
			err := backend.StartDisable(tt.args.userId, tt.args.now)
			if (err != nil) != tt.wantErr {
				t.Errorf("StartDisable() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr {
				assert.Equal(t, backend, tt.want, "StartDisable() got = %v, want %v", backend, tt.want)
			}
		})
	}
}

func TestEnableFailed(t *testing.T) {
	type fields struct {
		status         chaos.ChaosBackendStatus
		type2Operation map[constant.ChaosBackendOperationType]*vo.BackendOperation
	}
	type args struct {
		msg string
		now time.Time
	}
	testTime := time.Now()
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *Backend
		wantErr bool
	}{
		{
			name: "should be ok",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALLING,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						Status: constant.ChaosBackendOperationStatusStart,
					},
				},
			},
			args: args{
				msg: "install failed",
				now: testTime,
			},
			want: &Backend{
				Status:  chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALL_FAILED,
				Message: "install failed",
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						Status:      constant.ChaosBackendOperationStatusDone,
						CompletedAt: &testTime,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be failed, because the status is not installing",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
			},
			args: args{
				now: testTime,
			},
			want:    nil,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			backend := &Backend{
				Status:         tt.fields.status,
				Type2Operation: tt.fields.type2Operation,
			}
			err := backend.EnableFailed(tt.args.msg, tt.args.now)
			if (err != nil) != tt.wantErr {
				t.Errorf("EnableFailed() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr {
				assert.Equal(t, backend, tt.want, "EnableFailed() got = %v, want %v", backend, tt.want)
			}
		})
	}
}

func TestFinishDisableForce(t *testing.T) {
	type fields struct {
		status         chaos.ChaosBackendStatus
		type2Operation map[constant.ChaosBackendOperationType]*vo.BackendOperation
	}
	type args struct {
		now time.Time
	}
	testTime := time.Now()
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *Backend
		wantErr bool
	}{
		{
			name: "should be ok, when the backend is uninstalling",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_UNINSTALLING,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeDisable: {
						Status: constant.ChaosBackendOperationStatusStart,
					},
				},
			},
			args: args{
				now: testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeDisable: {
						Status:      constant.ChaosBackendOperationStatusDone,
						CompletedAt: &testTime,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be ok, when the backend is installing",
			fields: fields{
				status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALLING,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						Status: constant.ChaosBackendOperationStatusStart,
					},
				},
			},
			args: args{
				now: testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeEnable: {
						Status:      constant.ChaosBackendOperationStatusDone,
						CompletedAt: &testTime,
					},
					constant.ChaosBackendOperationTypeDisable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeDisable,
						Status:      constant.ChaosBackendOperationStatusDone,
						CreatedAt:   time.Time{},
						CreatedByID: 0,
						CompletedAt: &testTime,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be ok, when the backend is ready",
			fields: fields{
				status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_READY,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			args: args{
				now: testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeDisable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeDisable,
						Status:      constant.ChaosBackendOperationStatusDone,
						CreatedAt:   time.Time{},
						CreatedByID: 0,
						CompletedAt: &testTime,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be ok, when the backend is install_failed",
			fields: fields{
				status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_INSTALL_FAILED,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			args: args{
				now: testTime,
			},
			want: &Backend{
				Status: chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{
					constant.ChaosBackendOperationTypeDisable: {
						ID:          0,
						Type:        constant.ChaosBackendOperationTypeDisable,
						Status:      constant.ChaosBackendOperationStatusDone,
						CreatedAt:   time.Time{},
						CreatedByID: 0,
						CompletedAt: &testTime,
					},
				},
			},
			wantErr: false,
		},
		{
			name: "should be ok, when the backend is disabled",
			fields: fields{
				status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			args: args{
				now: testTime,
			},
			want: &Backend{
				Status:         chaos.ChaosBackendStatus_CHAOS_BACKEND_STATUS_DISABLED,
				Type2Operation: map[constant.ChaosBackendOperationType]*vo.BackendOperation{},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			backend := &Backend{
				Status:         tt.fields.status,
				Type2Operation: tt.fields.type2Operation,
			}
			err := backend.FinishDisableForce(tt.args.now)
			if (err != nil) != tt.wantErr {
				t.Errorf("FinishDisableForce() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr {
				assert.Equal(t, backend, tt.want, "FinishDisableForce() got = %v, want %v", backend, tt.want)
			}
		})
	}
}
