package routing

import (
	"fmt"
	"regexp"
	"strings"
)

// JSONPathResolver resolves JSONPath expressions like ${input.repo}, ${Clone.output.path}
type JSONPathResolver struct {
	input       map[string]interface{}
	stepResults map[string]interface{}
}

// NewJSONPathResolver creates a new resolver with input and step results
func NewJSONPathResolver(input map[string]interface{}, stepResults map[string]interface{}) *JSONPathResolver {
	return &JSONPathResolver{
		input:       input,
		stepResults: stepResults,
	}
}

// Resolve resolves a single JSONPath expression
// Supports: ${input.field}, ${StepName.output.field}, ${StepName.output.nested.field}
func (r *JSONPathResolver) Resolve(expr string) (interface{}, error) {
	if expr == "" {
		return nil, fmt.Errorf("expression cannot be empty")
	}

	// Check if it's a template expression (starts with ${ and ends with })
	if !strings.HasPrefix(expr, "${") || !strings.HasSuffix(expr, "}") {
		// Return as-is if not a template
		return expr, nil
	}

	// Extract the path from ${...}
	path := strings.TrimPrefix(expr, "${")
	path = strings.TrimSuffix(path, "}")

	return r.resolvePath(path)
}

// ResolveString resolves a string that may contain multiple JSONPath expressions
// Example: "Analysis at ${Clone.output.path} completed"
func (r *JSONPathResolver) ResolveString(str string) (string, error) {
	// Find all ${...} patterns
	pattern := regexp.MustCompile(`\$\{[^}]+\}`)
	
	result := str
	matches := pattern.FindAllString(str, -1)
	
	for _, match := range matches {
		value, err := r.Resolve(match)
		if err != nil {
			return "", err
		}
		
		// Convert value to string
		strValue := fmt.Sprintf("%v", value)
		result = strings.ReplaceAll(result, match, strValue)
	}
	
	return result, nil
}

// ResolvePaths resolves all JSONPath expressions in a map recursively
func (r *JSONPathResolver) ResolvePaths(data map[string]interface{}) (map[string]interface{}, error) {
	result := make(map[string]interface{})
	
	for key, value := range data {
		resolved, err := r.resolveValue(value)
		if err != nil {
			return nil, fmt.Errorf("failed to resolve key '%s': %w", key, err)
		}
		result[key] = resolved
	}
	
	return result, nil
}

// resolvePath resolves a dot-separated path
// Paths can be: input.field, StepName.output.field, etc.
func (r *JSONPathResolver) resolvePath(path string) (interface{}, error) {
	parts := strings.Split(path, ".")
	if len(parts) == 0 {
		return nil, fmt.Errorf("invalid path: %s", path)
	}

	// Check if first part is "input"
	if parts[0] == "input" {
		return r.resolveFromInput(parts[1:])
	}

	// Otherwise, assume it's a step name
	stepName := parts[0]
	stepData, ok := r.stepResults[stepName]
	if !ok {
		return nil, fmt.Errorf("step '%s' not found in results", stepName)
	}

	// Navigate through remaining parts
	return r.navigateObject(stepData, parts[1:])
}

// resolveFromInput resolves path from input data
func (r *JSONPathResolver) resolveFromInput(parts []string) (interface{}, error) {
	if len(parts) == 0 {
		return r.input, nil
	}

	return r.navigateObject(r.input, parts)
}

// navigateObject navigates through an object using path parts
func (r *JSONPathResolver) navigateObject(obj interface{}, parts []string) (interface{}, error) {
	current := obj

	for i, part := range parts {
		if current == nil {
			return nil, fmt.Errorf("cannot navigate through nil at part %d (%s)", i, part)
		}

		// Handle map
		if mapObj, ok := current.(map[string]interface{}); ok {
			value, exists := mapObj[part]
			if !exists {
				return nil, fmt.Errorf("key '%s' not found in object", part)
			}
			current = value
			continue
		}

		// Handle map[string]string
		if strMap, ok := current.(map[string]string); ok {
			value, exists := strMap[part]
			if !exists {
				return nil, fmt.Errorf("key '%s' not found in string map", part)
			}
			current = value
			continue
		}

		// Cannot navigate further
		return nil, fmt.Errorf("cannot navigate through non-object type at part %d (%s)", i, part)
	}

	return current, nil
}

// resolveValue recursively resolves values (strings, maps, slices)
func (r *JSONPathResolver) resolveValue(value interface{}) (interface{}, error) {
	if value == nil {
		return nil, nil
	}

	switch v := value.(type) {
	case string:
		// Try to resolve as JSONPath
		if strings.Contains(v, "${") {
			// Check if it's a pure template (only one expression filling the whole string)
			if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") && strings.Count(v, "${") == 1 {
				// Pure template - resolve as object
				resolved, err := r.Resolve(v)
				if err != nil {
					return nil, err
				}
				return resolved, nil
			}
			// String with embedded expressions - resolve as string
			resolved, err := r.ResolveString(v)
			if err != nil {
				return nil, err
			}
			return resolved, nil
		}
		return v, nil

	case map[string]interface{}:
		// Recursively resolve map
		return r.ResolvePaths(v)

	case []interface{}:
		// Recursively resolve slice
		result := make([]interface{}, len(v))
		for i, item := range v {
			resolved, err := r.resolveValue(item)
			if err != nil {
				return nil, err
			}
			result[i] = resolved
		}
		return result, nil

	default:
		// Return as-is for other types
		return v, nil
	}
}

// ValidatePath checks if a path is valid (doesn't guarantee it resolves)
func (r *JSONPathResolver) ValidatePath(path string) error {
	if !strings.Contains(path, ".") && path != "input" {
		return fmt.Errorf("invalid path: must contain '.' or be 'input'")
	}

	parts := strings.Split(path, ".")
	if len(parts) == 0 {
		return fmt.Errorf("invalid path: no parts")
	}

	return nil
}

// GetAvailableSteps returns list of available steps in step results
func (r *JSONPathResolver) GetAvailableSteps() []string {
	steps := make([]string, 0, len(r.stepResults))
	for step := range r.stepResults {
		steps = append(steps, step)
	}
	return steps
}

// GetInputFields returns list of available input fields
func (r *JSONPathResolver) GetInputFields() []string {
	fields := make([]string, 0, len(r.input))
	for field := range r.input {
		fields = append(fields, field)
	}
	return fields
}
