package workload

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/pov-service/internal/common/constant"
	"github.com/tidbcloud/pov-service/internal/model"
)

func TestValidateInjectFailureParams(t *testing.T) {
	tests := []struct {
		name    string
		toValue string
		wantErr bool
	}{
		{
			name:    "Valid JSON",
			toValue: `{"duration": "5m"}`,
			wantErr: false,
		},
		{
			name:    "Valid JSON with seconds",
			toValue: `{"duration": "30s"}`,
			wantErr: false,
		},
		{
			name:    "Valid JSON with hours",
			toValue: `{"duration": "2h"}`,
			wantErr: false,
		},
		{
			name:    "Empty toValue",
			toValue: "",
			wantErr: true,
		},
		{
			name:    "Invalid JSON",
			toValue: `{"duration": "5m"`,
			wantErr: true,
		},
		{
			name:    "Missing duration",
			toValue: `{}`,
			wantErr: true,
		},
		{
			name:    "Invalid duration format",
			toValue: `{"duration": "invalid"}`,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Test the validation logic that would be used in frontend
			if tt.toValue == "" {
				assert.True(t, tt.wantErr, "Expected error for empty toValue")
				return
			}

			var params struct {
				Duration string `json:"duration"`
			}
			err := json.Unmarshal([]byte(tt.toValue), &params)
			if err != nil {
				assert.True(t, tt.wantErr, "Expected JSON parsing error")
				return
			}

			if params.Duration == "" {
				assert.True(t, tt.wantErr, "Expected error for missing duration")
				return
			}

			_, err = time.ParseDuration(params.Duration)
			if err != nil {
				assert.True(t, tt.wantErr, "Expected duration parsing error")
			} else {
				assert.False(t, tt.wantErr, "Expected no error for valid duration")
			}
		})
	}
}

func TestScriptWithFailureOperations(t *testing.T) {
	// Test script with inject failure
	t.Run("Inject Failure", func(t *testing.T) {
		script := model.Script{
			ID: 1,
			ScriptSpec: model.ScriptSpec{
				Phases: []model.ScriptPhase{
					&model.ScriptMutatePhase{
						Name:          "Inject Failure Phase",
						ID:            1,
						State:         "Injecting failure",
						Description:   "Test inject failure",
						Message:       "Injecting failure for testing",
						StartDuration: 5 * time.Minute,
						Mutations: []model.ScriptMutation{
							{
								Param:   constant.InjectFailure,
								ToValue: `{"duration": "5m"}`,
							},
						},
					},
				},
			},
		}

		// Verify that script can be created with inject failure
		mutatePhases := script.GetMutatePhases()
		assert.Len(t, mutatePhases, 1)
		assert.Equal(t, "Inject Failure Phase", mutatePhases[0].Name)
		assert.Equal(t, constant.InjectFailure, mutatePhases[0].Mutations[0].Param)

		// Verify JSON parsing
		var failureParams struct {
			Duration string `json:"duration"`
		}
		err := json.Unmarshal([]byte(mutatePhases[0].Mutations[0].ToValue), &failureParams)
		assert.NoError(t, err)
		assert.Equal(t, "5m", failureParams.Duration)
	})

	// Test script with rollback failure
	t.Run("Rollback Failure", func(t *testing.T) {
		script := model.Script{
			ID: 2,
			ScriptSpec: model.ScriptSpec{
				Phases: []model.ScriptPhase{
					&model.ScriptMutatePhase{
						Name:          "Rollback Failure Phase",
						ID:            2,
						State:         "Rolling back failure",
						Description:   "Test rollback failure",
						Message:       "Rolling back failure for testing",
						StartDuration: 10 * time.Minute,
						Mutations: []model.ScriptMutation{
							{
								Param:   constant.RollbackFailure,
								ToValue: "",
							},
						},
					},
				},
			},
		}

		// Verify that script can be created with rollback failure
		mutatePhases := script.GetMutatePhases()
		assert.Len(t, mutatePhases, 1)
		assert.Equal(t, "Rollback Failure Phase", mutatePhases[0].Name)
		assert.Equal(t, constant.RollbackFailure, mutatePhases[0].Mutations[0].Param)
		assert.Equal(t, "", mutatePhases[0].Mutations[0].ToValue)
	})

	// Test script with mixed operations
	t.Run("Mixed Operations", func(t *testing.T) {
		script := model.Script{
			ID: 3,
			ScriptSpec: model.ScriptSpec{
				Phases: []model.ScriptPhase{
					&model.ScriptMutatePhase{
						Name:          "Mixed Phase",
						ID:            3,
						State:         "Mixed operations",
						Description:   "Test mixed operations",
						Message:       "Testing mixed operations",
						StartDuration: 2 * time.Minute,
						Mutations: []model.ScriptMutation{
							{
								Param:   constant.MaxRU,
								ToValue: "2000",
							},
							{
								Param:   constant.InjectFailure,
								ToValue: `{"duration": "3m"}`,
							},
							{
								Param:   constant.RollbackFailure,
								ToValue: "",
							},
						},
					},
				},
			},
		}

		// Verify that script can be created with mixed operations
		mutatePhases := script.GetMutatePhases()
		assert.Len(t, mutatePhases, 1)
		assert.Equal(t, "Mixed Phase", mutatePhases[0].Name)
		assert.Len(t, mutatePhases[0].Mutations, 3)

		// Verify each mutation type
		mutations := mutatePhases[0].Mutations
		assert.Equal(t, constant.MaxRU, mutations[0].Param)
		assert.Equal(t, constant.InjectFailure, mutations[1].Param)
		assert.Equal(t, constant.RollbackFailure, mutations[2].Param)

		// Verify inject failure JSON
		var failureParams struct {
			Duration string `json:"duration"`
		}
		err := json.Unmarshal([]byte(mutations[1].ToValue), &failureParams)
		assert.NoError(t, err)
		assert.Equal(t, "3m", failureParams.Duration)
	})
}
