package routing

import (
	"encoding/json"
	"testing"
)

func TestWorkflowSpecMarshal(t *testing.T) {
	spec := WorkflowSpec{
		Name: "test-workflow",
		Input: map[string]interface{}{
			"repo": "https://github.com/test/repo",
		},
		States: []State{
			{
				Name:     "Clone",
				Type:     StateTypeTask,
				Resource: "CloneRepoActivity",
				Parameters: map[string]interface{}{
					"repo": "${input.repo}",
				},
				Timeout: "5m",
				Next:    "Analyze",
			},
		},
	}

	// Marshal to JSON
	data, err := json.Marshal(spec)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	// Unmarshal back
	var spec2 WorkflowSpec
	err = json.Unmarshal(data, &spec2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	// Verify
	if spec2.Name != spec.Name {
		t.Errorf("Name mismatch: %s != %s", spec2.Name, spec.Name)
	}
	if len(spec2.States) != len(spec.States) {
		t.Errorf("State count mismatch: %d != %d", len(spec2.States), len(spec.States))
	}
}

func TestCronWorkflowSpecMarshal(t *testing.T) {
	spec := CronWorkflowSpec{
		Name:          "daily-scan",
		Type:          "CronWorkflow",
		Schedule:      "0 2 * * *",
		Timezone:      "UTC",
		MaxConcurrent: 1,
		EnableHistory: true,
		Input: map[string]interface{}{
			"repos": []string{"repo1", "repo2"},
		},
	}

	// Marshal to JSON
	data, err := json.Marshal(spec)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	// Unmarshal back
	var spec2 CronWorkflowSpec
	err = json.Unmarshal(data, &spec2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	// Verify
	if spec2.Schedule != spec.Schedule {
		t.Errorf("Schedule mismatch: %s != %s", spec2.Schedule, spec.Schedule)
	}
	if spec2.Timezone != spec.Timezone {
		t.Errorf("Timezone mismatch: %s != %s", spec2.Timezone, spec.Timezone)
	}
	if spec2.EnableHistory != spec.EnableHistory {
		t.Errorf("EnableHistory mismatch: %v != %v", spec2.EnableHistory, spec.EnableHistory)
	}
}

func TestRetryPolicyMarshal(t *testing.T) {
	policy := RetryPolicy{
		MaxAttempts:     3,
		BackoffRate:     2.0,
		InitialInterval: "1s",
		MaxInterval:     "1m",
	}

	data, err := json.Marshal(policy)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	var policy2 RetryPolicy
	err = json.Unmarshal(data, &policy2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	if policy2.MaxAttempts != policy.MaxAttempts {
		t.Errorf("MaxAttempts mismatch: %d != %d", policy2.MaxAttempts, policy.MaxAttempts)
	}
	if policy2.BackoffRate != policy.BackoffRate {
		t.Errorf("BackoffRate mismatch: %f != %f", policy2.BackoffRate, policy.BackoffRate)
	}
}

func TestStateMarshal(t *testing.T) {
	state := State{
		Name:     "Analyze",
		Type:     StateTypeTask,
		Resource: "AnalyzeCodeActivity",
		Parameters: map[string]interface{}{
			"path": "${Clone.output.path}",
		},
		Timeout: "10m",
		Retry: &RetryPolicy{
			MaxAttempts:     3,
			BackoffRate:     2.0,
			InitialInterval: "1s",
		},
		Catch: []CatchClause{
			{
				ErrorEquals: []string{"Timeout"},
				Next:        "HandleTimeout",
			},
		},
		Next: "Judge",
	}

	data, err := json.Marshal(state)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	var state2 State
	err = json.Unmarshal(data, &state2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	if state2.Name != state.Name {
		t.Errorf("Name mismatch: %s != %s", state2.Name, state.Name)
	}
	if state2.Type != state.Type {
		t.Errorf("Type mismatch: %s != %s", state2.Type, state.Type)
	}
	if len(state2.Catch) != len(state.Catch) {
		t.Errorf("Catch count mismatch: %d != %d", len(state2.Catch), len(state.Catch))
	}
}

func TestPassState(t *testing.T) {
	state := State{
		Name:   "SetSuccess",
		Type:   StateTypePass,
		Result: map[string]interface{}{"status": "success"},
		End:    true,
	}

	data, err := json.Marshal(state)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	var state2 State
	err = json.Unmarshal(data, &state2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	if state2.Type != StateTypePass {
		t.Errorf("Type should be Pass, got: %s", state2.Type)
	}
	if !state2.End {
		t.Error("End should be true")
	}
}

func TestFailState(t *testing.T) {
	state := State{
		Name:  "HandleError",
		Type:  StateTypeFail,
		Error: "InvalidInput",
		Cause: "Repository URL is invalid",
	}

	data, err := json.Marshal(state)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	var state2 State
	err = json.Unmarshal(data, &state2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	if state2.Type != StateTypeFail {
		t.Errorf("Type should be Fail, got: %s", state2.Type)
	}
	if state2.Error != "InvalidInput" {
		t.Errorf("Error mismatch: %s != InvalidInput", state2.Error)
	}
}

func TestExecutionContextInit(t *testing.T) {
	ec := &ExecutionContext{
		Input:       map[string]interface{}{"repo": "test"},
		StepResults: make(map[string]interface{}),
		History:     make([]ExecutionEvent, 0),
	}

	if ec.Input == nil {
		t.Error("Input should not be nil")
	}
	if ec.StepResults == nil {
		t.Error("StepResults should not be nil")
	}
	if ec.History == nil {
		t.Error("History should not be nil")
	}
}

func TestComplexWorkflowSpec(t *testing.T) {
	// Test a realistic workflow spec
	spec := WorkflowSpec{
		Name: "code-review",
		Input: map[string]interface{}{
			"repo":   "https://github.com/rockliang/poimen",
			"branch": "feature/x",
		},
		States: []State{
			{
				Name:     "Clone",
				Type:     StateTypeTask,
				Resource: "CloneRepoActivity",
				Parameters: map[string]interface{}{
					"repo":   "${input.repo}",
					"branch": "${input.branch}",
				},
				Timeout: "5m",
				Retry: &RetryPolicy{
					MaxAttempts:     2,
					BackoffRate:     1.5,
					InitialInterval: "1s",
				},
				Next: "Analyze",
			},
			{
				Name:     "Analyze",
				Type:     StateTypeTask,
				Resource: "AnalyzeCodeActivity",
				Parameters: map[string]interface{}{
					"path": "${Clone.output.path}",
				},
				Timeout: "10m",
				Retry: &RetryPolicy{
					MaxAttempts:     3,
					BackoffRate:     2.0,
					InitialInterval: "1s",
				},
				Catch: []CatchClause{
					{
						ErrorEquals: []string{"Timeout"},
						Next:        "HandleTimeout",
					},
				},
				Next: "Judge",
			},
			{
				Name:     "Judge",
				Type:     StateTypeTask,
				Resource: "JudgeActivity",
				Parameters: map[string]interface{}{
					"quality": "${Analyze.output.quality}",
				},
				Timeout: "5m",
				End:     true,
			},
			{
				Name:  "HandleTimeout",
				Type:  StateTypeFail,
				Error: "AnalysisTimeout",
				Cause: "Code analysis timed out",
			},
		},
	}

	// Marshal
	data, err := json.Marshal(spec)
	if err != nil {
		t.Fatalf("Failed to marshal: %v", err)
	}

	// Unmarshal
	var spec2 WorkflowSpec
	err = json.Unmarshal(data, &spec2)
	if err != nil {
		t.Fatalf("Failed to unmarshal: %v", err)
	}

	// Verify
	if spec2.Name != "code-review" {
		t.Errorf("Name mismatch")
	}
	if len(spec2.States) != 4 {
		t.Errorf("Expected 4 states, got %d", len(spec2.States))
	}

	// Check first state
	if spec2.States[0].Resource != "CloneRepoActivity" {
		t.Errorf("First state resource mismatch")
	}

	// Check error catching
	if len(spec2.States[1].Catch) == 0 {
		t.Error("Analyze state should have catch clauses")
	}
}
