package routing

import (
	"strings"
	"testing"
)

func TestResolveInputField(t *testing.T) {
	input := map[string]interface{}{
		"repo":   "https://github.com/test/repo",
		"branch": "main",
	}
	resolver := NewJSONPathResolver(input, map[string]interface{}{})

	// Test resolving input field
	value, err := resolver.Resolve("${input.repo}")
	if err != nil {
		t.Fatalf("Failed to resolve: %v", err)
	}

	if value != "https://github.com/test/repo" {
		t.Errorf("Expected 'https://github.com/test/repo', got %v", value)
	}
}

func TestResolveNestedField(t *testing.T) {
	input := map[string]interface{}{}
	stepResults := map[string]interface{}{
		"Clone": map[string]interface{}{
			"output": map[string]interface{}{
				"path":   "/tmp/repo",
				"commit": "abc123",
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Test resolving nested field
	value, err := resolver.Resolve("${Clone.output.path}")
	if err != nil {
		t.Fatalf("Failed to resolve: %v", err)
	}

	if value != "/tmp/repo" {
		t.Errorf("Expected '/tmp/repo', got %v", value)
	}
}

func TestResolveDeepNesting(t *testing.T) {
	input := map[string]interface{}{}
	stepResults := map[string]interface{}{
		"Analyze": map[string]interface{}{
			"output": map[string]interface{}{
				"metrics": map[string]interface{}{
					"quality": map[string]interface{}{
						"score": 0.95,
					},
				},
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	value, err := resolver.Resolve("${Analyze.output.metrics.quality.score}")
	if err != nil {
		t.Fatalf("Failed to resolve: %v", err)
	}

	score, ok := value.(float64)
	if !ok {
		t.Fatalf("Expected float64, got %T", value)
	}

	if score != 0.95 {
		t.Errorf("Expected 0.95, got %v", score)
	}
}

func TestResolveNonTemplate(t *testing.T) {
	input := map[string]interface{}{}
	resolver := NewJSONPathResolver(input, map[string]interface{}{})

	// Non-template strings should be returned as-is
	value, err := resolver.Resolve("plain string")
	if err != nil {
		t.Fatalf("Failed to resolve: %v", err)
	}

	if value != "plain string" {
		t.Errorf("Expected 'plain string', got %v", value)
	}
}

func TestResolveMissingStep(t *testing.T) {
	input := map[string]interface{}{}
	resolver := NewJSONPathResolver(input, map[string]interface{}{})

	// Should error on missing step
	_, err := resolver.Resolve("${NonExistentStep.output.field}")
	if err == nil {
		t.Error("Expected error for missing step")
	}
}

func TestResolveMissingField(t *testing.T) {
	input := map[string]interface{}{}
	stepResults := map[string]interface{}{
		"Clone": map[string]interface{}{
			"output": map[string]interface{}{
				"path": "/tmp/repo",
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Should error on missing field
	_, err := resolver.Resolve("${Clone.output.nonexistent}")
	if err == nil {
		t.Error("Expected error for missing field")
	}
}

func TestResolveString(t *testing.T) {
	input := map[string]interface{}{
		"repo": "https://github.com/test/repo",
	}
	stepResults := map[string]interface{}{
		"Clone": map[string]interface{}{
			"output": map[string]interface{}{
				"path": "/tmp/repo",
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Resolve string with multiple expressions
	result, err := resolver.ResolveString("Repository at ${input.repo} cloned to ${Clone.output.path}")
	if err != nil {
		t.Fatalf("Failed to resolve string: %v", err)
	}

	expected := "Repository at https://github.com/test/repo cloned to /tmp/repo"
	if result != expected {
		t.Errorf("Expected '%s', got '%s'", expected, result)
	}
}

func TestResolveStringNoExpressions(t *testing.T) {
	input := map[string]interface{}{}
	resolver := NewJSONPathResolver(input, map[string]interface{}{})

	// String without expressions should be returned unchanged
	result, err := resolver.ResolveString("plain string")
	if err != nil {
		t.Fatalf("Failed to resolve string: %v", err)
	}

	if result != "plain string" {
		t.Errorf("Expected 'plain string', got '%s'", result)
	}
}

func TestResolvePaths(t *testing.T) {
	input := map[string]interface{}{
		"repo": "https://github.com/test/repo",
	}
	stepResults := map[string]interface{}{
		"Clone": map[string]interface{}{
			"output": map[string]interface{}{
				"path": "/tmp/repo",
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Resolve a map with JSONPath values
	data := map[string]interface{}{
		"repository": "${input.repo}",
		"path":       "${Clone.output.path}",
		"literal":    "just a string",
	}

	result, err := resolver.ResolvePaths(data)
	if err != nil {
		t.Fatalf("Failed to resolve paths: %v", err)
	}

	if result["repository"] != "https://github.com/test/repo" {
		t.Errorf("repository mismatch: %v", result["repository"])
	}

	if result["path"] != "/tmp/repo" {
		t.Errorf("path mismatch: %v", result["path"])
	}

	if result["literal"] != "just a string" {
		t.Errorf("literal mismatch: %v", result["literal"])
	}
}

func TestResolveNestedMap(t *testing.T) {
	input := map[string]interface{}{}
	stepResults := map[string]interface{}{
		"Analyze": map[string]interface{}{
			"output": map[string]interface{}{
				"score": 0.95,
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Resolve nested map
	data := map[string]interface{}{
		"analysis": map[string]interface{}{
			"quality": "${Analyze.output.score}",
		},
	}

	result, err := resolver.ResolvePaths(data)
	if err != nil {
		t.Fatalf("Failed to resolve nested map: %v", err)
	}

	analysisMap := result["analysis"].(map[string]interface{})
	if analysisMap["quality"] != 0.95 {
		t.Errorf("Expected 0.95, got %v", analysisMap["quality"])
	}
}

func TestResolveSlice(t *testing.T) {
	input := map[string]interface{}{}
	stepResults := map[string]interface{}{
		"Scan": map[string]interface{}{
			"output": map[string]interface{}{
				"vulnerabilities": []map[string]interface{}{
					{"cve": "CVE-001"},
					{"cve": "CVE-002"},
				},
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Resolve slice
	data := map[string]interface{}{
		"issues": "${Scan.output.vulnerabilities}",
	}

	result, err := resolver.ResolvePaths(data)
	if err != nil {
		t.Fatalf("Failed to resolve slice: %v", err)
	}

	issues := result["issues"].([]map[string]interface{})
	if len(issues) != 2 {
		t.Errorf("Expected 2 issues, got %d", len(issues))
	}
}

func TestValidatePath(t *testing.T) {
	resolver := NewJSONPathResolver(map[string]interface{}{}, map[string]interface{}{})

	// Valid paths
	validPaths := []string{
		"input.repo",
		"Clone.output.path",
		"Analyze.output.metrics.quality.score",
	}

	for _, path := range validPaths {
		if err := resolver.ValidatePath(path); err != nil {
			t.Errorf("Path '%s' should be valid: %v", path, err)
		}
	}

	// Invalid paths
	invalidPaths := []string{
		"",
		"singleword",
	}

	for _, path := range invalidPaths {
		if err := resolver.ValidatePath(path); err == nil {
			t.Errorf("Path '%s' should be invalid", path)
		}
	}
}

func TestGetAvailableSteps(t *testing.T) {
	stepResults := map[string]interface{}{
		"Clone":   map[string]interface{}{},
		"Analyze": map[string]interface{}{},
		"Scan":    map[string]interface{}{},
	}
	resolver := NewJSONPathResolver(map[string]interface{}{}, stepResults)

	steps := resolver.GetAvailableSteps()
	if len(steps) != 3 {
		t.Errorf("Expected 3 steps, got %d", len(steps))
	}

	// Check all steps are present
	stepMap := make(map[string]bool)
	for _, step := range steps {
		stepMap[step] = true
	}

	if !stepMap["Clone"] || !stepMap["Analyze"] || !stepMap["Scan"] {
		t.Error("Missing expected steps")
	}
}

func TestGetInputFields(t *testing.T) {
	input := map[string]interface{}{
		"repo":   "test",
		"branch": "main",
		"path":   "/tmp",
	}
	resolver := NewJSONPathResolver(input, map[string]interface{}{})

	fields := resolver.GetInputFields()
	if len(fields) != 3 {
		t.Errorf("Expected 3 fields, got %d", len(fields))
	}

	// Check all fields are present
	fieldMap := make(map[string]bool)
	for _, field := range fields {
		fieldMap[field] = true
	}

	if !fieldMap["repo"] || !fieldMap["branch"] || !fieldMap["path"] {
		t.Error("Missing expected input fields")
	}
}

func TestResolveWithStringMap(t *testing.T) {
	input := map[string]interface{}{}
	stepResults := map[string]interface{}{
		"Config": map[string]string{
			"url":  "https://example.com",
			"port": "8080",
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Resolve from string map
	value, err := resolver.Resolve("${Config.url}")
	if err != nil {
		t.Fatalf("Failed to resolve: %v", err)
	}

	if value != "https://example.com" {
		t.Errorf("Expected 'https://example.com', got %v", value)
	}
}

func TestResolveComplexWorkflow(t *testing.T) {
	input := map[string]interface{}{
		"repo":   "https://github.com/test/repo",
		"branch": "feature/new",
	}
	stepResults := map[string]interface{}{
		"Clone": map[string]interface{}{
			"output": map[string]interface{}{
				"path":   "/tmp/repo",
				"commit": "abc123def456",
			},
		},
		"Analyze": map[string]interface{}{
			"output": map[string]interface{}{
				"quality": 0.92,
				"issues": []string{"issue1", "issue2"},
			},
		},
	}
	resolver := NewJSONPathResolver(input, stepResults)

	// Complex workflow parameters
	params := map[string]interface{}{
		"source_repo": "${input.repo}",
		"target_branch": "${input.branch}",
		"cloned_path": "${Clone.output.path}",
		"commit_hash": "${Clone.output.commit}",
		"quality_score": "${Analyze.output.quality}",
		"issues_found": "${Analyze.output.issues}",
		"report": "Quality score is ${Analyze.output.quality} for commit ${Clone.output.commit}",
	}

	resolved, err := resolver.ResolvePaths(params)
	if err != nil {
		t.Fatalf("Failed to resolve workflow: %v", err)
	}

	if resolved["source_repo"] != "https://github.com/test/repo" {
		t.Error("source_repo mismatch")
	}

	if resolved["target_branch"] != "feature/new" {
		t.Error("target_branch mismatch")
	}

	if resolved["cloned_path"] != "/tmp/repo" {
		t.Error("cloned_path mismatch")
	}

	if resolved["commit_hash"] != "abc123def456" {
		t.Error("commit_hash mismatch")
	}

	if resolved["quality_score"] != 0.92 {
		t.Error("quality_score mismatch")
	}

	// Check report string resolution
	report := resolved["report"].(string)
	if !strings.Contains(report, "0.92") || !strings.Contains(report, "abc123def456") {
		t.Errorf("Report not properly resolved: %s", report)
	}
}

func TestResolveEmptyInput(t *testing.T) {
	input := map[string]interface{}{}
	resolver := NewJSONPathResolver(input, map[string]interface{}{})

	// Should resolve to just input when accessing input
	value, err := resolver.Resolve("${input}")
	if err != nil {
		t.Fatalf("Failed to resolve: %v", err)
	}

	// Should be empty map
	inputMap, ok := value.(map[string]interface{})
	if !ok || len(inputMap) != 0 {
		t.Error("Expected empty input map")
	}
}
