package workflow

import (
	"testing"
)

func TestParseWorkflowDefinition(t *testing.T) {
	tests := []struct {
		name    string
		jsonDef string
		wantErr bool
	}{
		{
			name: "valid simple workflow",
			jsonDef: `{
				"nodes": {
					"start": {
						"type": "task",
						"title": "Start",
						"content": "Start task",
						"dependsOn": []
					},
					"end": {
						"type": "task",
						"title": "End",
						"content": "End task",
						"dependsOn": ["start"]
					}
				}
			}`,
			wantErr: false,
		},
		{
			name: "valid parallel workflow",
			jsonDef: `{
				"nodes": {
					"start": {
						"type": "task",
						"title": "Start",
						"content": "Start task",
						"dependsOn": []
					},
					"parallel1": {
						"type": "task",
						"title": "Parallel 1",
						"content": "Parallel task 1",
						"dependsOn": ["start"]
					},
					"parallel2": {
						"type": "task",
						"title": "Parallel 2",
						"content": "Parallel task 2",
						"dependsOn": ["start"]
					},
					"join": {
						"type": "join",
						"title": "Join",
						"content": "Join parallel branches",
						"dependsOn": ["parallel1", "parallel2"]
					}
				}
			}`,
			wantErr: false,
		},
		{
			name:    "invalid JSON",
			jsonDef: `{invalid}`,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			def, err := ParseWorkflowDefinition(tt.jsonDef)
			if (err != nil) != tt.wantErr {
				t.Errorf("ParseWorkflowDefinition() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !tt.wantErr && def == nil {
				t.Error("ParseWorkflowDefinition() returned nil definition")
			}
		})
	}
}

func TestParseAndValidate(t *testing.T) {
	tests := []struct {
		name    string
		jsonDef string
		wantErr bool
	}{
		{
			name:    "empty definition",
			jsonDef: `{}`,
			wantErr: true,
		},
		{
			name: "node missing type",
			jsonDef: `{
				"nodes": {
					"start": {
						"title": "Start"
					}
				}
			}`,
			wantErr: true,
		},
		{
			name: "invalid node type",
			jsonDef: `{
				"nodes": {
					"start": {
						"type": "invalid",
						"title": "Start"
					}
				}
			}`,
			wantErr: true,
		},
		{
			name: "dependency on non-existent node",
			jsonDef: `{
				"nodes": {
					"start": {
						"type": "task",
						"title": "Start",
						"dependsOn": ["nonexistent"]
					}
				}
			}`,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			def, err := ParseWorkflowDefinition(tt.jsonDef)
			if err != nil {
				if !tt.wantErr {
					t.Errorf("ParseWorkflowDefinition() unexpected error = %v", err)
				}
				return
			}
			// Parse succeeded, now validate
			err = def.Validate()
			if (err != nil) != tt.wantErr {
				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestValidate(t *testing.T) {
	tests := []struct {
		name    string
		jsonDef string
		wantErr bool
	}{
		{
			name: "valid workflow",
			jsonDef: `{
				"nodes": {
					"start": {"type": "task", "title": "Start", "dependsOn": []},
					"end": {"type": "task", "title": "End", "dependsOn": ["start"]}
				}
			}`,
			wantErr: false,
		},
		{
			name: "cycle detection",
			jsonDef: `{
				"nodes": {
					"a": {"type": "task", "title": "A", "dependsOn": ["b"]},
					"b": {"type": "task", "title": "B", "dependsOn": ["a"]}
				}
			}`,
			wantErr: true,
		},
		{
			name: "self-referential cycle",
			jsonDef: `{
				"nodes": {
					"a": {"type": "task", "title": "A", "dependsOn": ["a"]}
				}
			}`,
			wantErr: true,
		},
		{
			name: "complex cycle",
			jsonDef: `{
				"nodes": {
					"a": {"type": "task", "title": "A", "dependsOn": []},
					"b": {"type": "task", "title": "B", "dependsOn": ["a"]},
					"c": {"type": "task", "title": "C", "dependsOn": ["b"]},
					"d": {"type": "task", "title": "D", "dependsOn": ["c"]},
					"e": {"type": "task", "title": "E", "dependsOn": ["d", "b"]}
				}
			}`,
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			def, err := ParseWorkflowDefinition(tt.jsonDef)
			if err != nil {
				t.Fatalf("ParseWorkflowDefinition() error = %v", err)
			}

			err = def.Validate()
			if (err != nil) != tt.wantErr {
				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestGetRootNodes(t *testing.T) {
	jsonDef := `{
		"nodes": {
			"start": {"type": "task", "title": "Start", "dependsOn": []},
			"middle": {"type": "task", "title": "Middle", "dependsOn": ["start"]},
			"end": {"type": "task", "title": "End", "dependsOn": ["middle"]}
		}
	}`

	def, err := ParseWorkflowDefinition(jsonDef)
	if err != nil {
		t.Fatalf("ParseWorkflowDefinition() error = %v", err)
	}

	roots := def.GetRootNodes()
	if len(roots) != 1 {
		t.Errorf("GetRootNodes() returned %d roots, want 1", len(roots))
	}
	if len(roots) > 0 && roots[0] != "start" {
		t.Errorf("GetRootNodes() returned %v, want [start]", roots)
	}
}

func TestGetDependentNodes(t *testing.T) {
	jsonDef := `{
		"nodes": {
			"start": {"type": "task", "title": "Start", "dependsOn": []},
			"branch1": {"type": "task", "title": "Branch 1", "dependsOn": ["start"]},
			"branch2": {"type": "task", "title": "Branch 2", "dependsOn": ["start"]},
			"end": {"type": "task", "title": "End", "dependsOn": ["branch1", "branch2"]}
		}
	}`

	def, err := ParseWorkflowDefinition(jsonDef)
	if err != nil {
		t.Fatalf("ParseWorkflowDefinition() error = %v", err)
	}

	dependents := def.GetDependentNodes("start")
	if len(dependents) != 2 {
		t.Errorf("GetDependentNodes(start) returned %d dependents, want 2", len(dependents))
	}

	dependents = def.GetDependentNodes("branch1")
	if len(dependents) != 1 {
		t.Errorf("GetDependentNodes(branch1) returned %d dependents, want 1", len(dependents))
	}
}

func TestGetReadyNodes(t *testing.T) {
	jsonDef := `{
		"nodes": {
			"start": {"type": "task", "title": "Start", "dependsOn": []},
			"middle": {"type": "task", "title": "Middle", "dependsOn": ["start"]},
			"end": {"type": "task", "title": "End", "dependsOn": ["middle"]}
		}
	}`

	def, err := ParseWorkflowDefinition(jsonDef)
	if err != nil {
		t.Fatalf("ParseWorkflowDefinition() error = %v", err)
	}

	// Initially, only start should be ready
	ready := def.GetReadyNodes(map[string]bool{})
	if len(ready) != 1 || ready[0] != "start" {
		t.Errorf("GetReadyNodes({}) = %v, want [start]", ready)
	}

	// After start is executed, middle should be ready
	ready = def.GetReadyNodes(map[string]bool{"start": true})
	if len(ready) != 1 || ready[0] != "middle" {
		t.Errorf("GetReadyNodes({start:true}) = %v, want [middle]", ready)
	}

	// After middle is executed, end should be ready
	ready = def.GetReadyNodes(map[string]bool{"start": true, "middle": true})
	if len(ready) != 1 || ready[0] != "end" {
		t.Errorf("GetReadyNodes({start:true, middle:true}) = %v, want [end]", ready)
	}
}

func TestParallelNodeTypes(t *testing.T) {
	jsonDef := `{
		"nodes": {
			"start": {"type": "task", "title": "Start", "dependsOn": []},
			"parallel": {"type": "parallel", "title": "Parallel", "dependsOn": ["start"]},
			"join": {"type": "join", "title": "Join", "dependsOn": ["parallel"]}
		}
	}`

	def, err := ParseWorkflowDefinition(jsonDef)
	if err != nil {
		t.Fatalf("ParseWorkflowDefinition() error = %v", err)
	}

	if err := def.Validate(); err != nil {
		t.Errorf("Validate() error = %v, want nil", err)
	}

	// Verify node types
	if def.Nodes["parallel"].Type != NodeTypeParallel {
		t.Errorf("parallel node type = %v, want %v", def.Nodes["parallel"].Type, NodeTypeParallel)
	}
	if def.Nodes["join"].Type != NodeTypeJoin {
		t.Errorf("join node type = %v, want %v", def.Nodes["join"].Type, NodeTypeJoin)
	}
}

func TestConditionNodeType(t *testing.T) {
	jsonDef := `{
		"nodes": {
			"start": {"type": "task", "title": "Start", "dependsOn": []},
			"condition": {"type": "condition", "title": "Condition", "dependsOn": ["start"]}
		}
	}`

	def, err := ParseWorkflowDefinition(jsonDef)
	if err != nil {
		t.Fatalf("ParseWorkflowDefinition() error = %v", err)
	}

	if err := def.Validate(); err != nil {
		t.Errorf("Validate() error = %v, want nil", err)
	}

	if def.Nodes["condition"].Type != NodeTypeCondition {
		t.Errorf("condition node type = %v, want %v", def.Nodes["condition"].Type, NodeTypeCondition)
	}
}

func TestTriggerNodeType(t *testing.T) {
	jsonDef := `{
		"nodes": {
			"trigger": {"type": "trigger", "title": "Trigger", "dependsOn": []},
			"task": {"type": "task", "title": "Task", "dependsOn": ["trigger"]}
		}
	}`

	def, err := ParseWorkflowDefinition(jsonDef)
	if err != nil {
		t.Fatalf("ParseWorkflowDefinition() error = %v", err)
	}

	if err := def.Validate(); err != nil {
		t.Errorf("Validate() error = %v, want nil", err)
	}

	if def.Nodes["trigger"].Type != NodeTypeTrigger {
		t.Errorf("trigger node type = %v, want %v", def.Nodes["trigger"].Type, NodeTypeTrigger)
	}
}
