package routing

import (
	"fmt"
	"time"
)

// ValidationError represents a single validation error
type ValidationError struct {
	Path    string // JSONPath where error occurred
	Message string // Error message
}

// ValidationResult contains all validation errors
type ValidationResult struct {
	Valid  bool
	Errors []ValidationError
}

// Validator validates WorkflowSpec and CronWorkflowSpec
type Validator struct {
	kb *KnowledgeBase
}

// NewValidator creates a new validator with knowledge base
func NewValidator(kb *KnowledgeBase) *Validator {
	return &Validator{kb: kb}
}

// ValidateWorkflowSpec validates a one-time workflow spec
func (v *Validator) ValidateWorkflowSpec(spec *WorkflowSpec) *ValidationResult {
	result := &ValidationResult{
		Valid:  true,
		Errors: []ValidationError{},
	}

	if spec == nil {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec",
			Message: "workflow spec cannot be nil",
		})
		return result
	}

	// Validate name
	var errs []ValidationError
	if spec.Name == "" {
		result.Valid = false
		errs = append(errs, ValidationError{
			Path:    "spec.name",
			Message: "workflow name is required",
		})
	}

	// Validate states
	if len(spec.States) == 0 {
		result.Valid = false
		errs = append(errs, ValidationError{
			Path:    "spec.states",
			Message: "at least one state is required",
		})
	}

	// Validate each state
	stateNames := make(map[string]bool)

	for i, state := range spec.States {
		path := fmt.Sprintf("spec.states[%d]", i)
		
		if state.Name == "" {
			result.Valid = false
			errs = append(errs, ValidationError{
				Path:    path + ".name",
				Message: "state name is required",
			})
			continue
		}

		if stateNames[state.Name] {
			result.Valid = false
			errs = append(errs, ValidationError{
				Path:    path + ".name",
				Message: fmt.Sprintf("duplicate state name: %s", state.Name),
			})
		}
		stateNames[state.Name] = true

		// Validate state type
		stateErrs := v.validateState(state, path)
		if len(stateErrs) > 0 {
			result.Valid = false
			errs = append(errs, stateErrs...)
		}
	}

	// Validate state transitions
	for i, state := range spec.States {
		path := fmt.Sprintf("spec.states[%d]", i)
		
		// Check that Next state exists (if specified)
		if state.Next != "" && !stateNames[state.Next] {
			result.Valid = false
			errs = append(errs, ValidationError{
				Path:    path + ".next",
				Message: fmt.Sprintf("state '%s' does not exist", state.Next),
			})
		}

		// Check that Catch targets exist
		for j, catchClause := range state.Catch {
			if catchClause.Next != "" && !stateNames[catchClause.Next] {
				result.Valid = false
				errs = append(errs, ValidationError{
					Path:    path + fmt.Sprintf(".catch[%d].next", j),
					Message: fmt.Sprintf("state '%s' does not exist", catchClause.Next),
				})
			}
		}
	}

	result.Errors = errs
	return result
}

// ValidateCronWorkflowSpec validates a scheduled workflow spec
func (v *Validator) ValidateCronWorkflowSpec(spec *CronWorkflowSpec) *ValidationResult {
	result := &ValidationResult{
		Valid:  true,
		Errors: []ValidationError{},
	}

	if spec == nil {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec",
			Message: "cron workflow spec cannot be nil",
		})
		return result
	}

	// Validate name
	if spec.Name == "" {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec.name",
			Message: "workflow name is required",
		})
	}

	// Validate type
	if spec.Type != "CronWorkflow" {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec.type",
			Message: "type must be 'CronWorkflow'",
		})
	}

	// Validate cron expression
	if spec.Schedule == "" {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec.schedule",
			Message: "cron schedule is required",
		})
	} else {
		if err := validateCronExpression(spec.Schedule); err != nil {
			result.Valid = false
			result.Errors = append(result.Errors, ValidationError{
				Path:    "spec.schedule",
				Message: err.Error(),
			})
		}
	}

	// Validate timezone
	if spec.Timezone == "" {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec.timezone",
			Message: "timezone is required",
		})
	} else {
		if _, err := time.LoadLocation(spec.Timezone); err != nil {
			result.Valid = false
			result.Errors = append(result.Errors, ValidationError{
				Path:    "spec.timezone",
				Message: fmt.Sprintf("invalid timezone: %s", spec.Timezone),
			})
		}
	}

	// Validate maxConcurrent
	if spec.MaxConcurrent < 1 {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec.maxConcurrent",
			Message: "maxConcurrent must be >= 1",
		})
	}

	// Validate states (same as one-time workflow)
	if len(spec.States) == 0 {
		result.Valid = false
		result.Errors = append(result.Errors, ValidationError{
			Path:    "spec.states",
			Message: "at least one state is required",
		})
	}

	stateNames := make(map[string]bool)
	for i, state := range spec.States {
		path := fmt.Sprintf("spec.states[%d]", i)
		
		if state.Name == "" {
			result.Valid = false
			result.Errors = append(result.Errors, ValidationError{
				Path:    path + ".name",
				Message: "state name is required",
			})
			continue
		}

		if stateNames[state.Name] {
			result.Valid = false
			result.Errors = append(result.Errors, ValidationError{
				Path:    path + ".name",
				Message: fmt.Sprintf("duplicate state name: %s", state.Name),
			})
		}
		stateNames[state.Name] = true

		stateErrs := v.validateState(state, path)
		if len(stateErrs) > 0 {
			result.Valid = false
			result.Errors = append(result.Errors, stateErrs...)
		}
	}

	// Validate state transitions
	for i, state := range spec.States {
		path := fmt.Sprintf("spec.states[%d]", i)
		
		if state.Next != "" && !stateNames[state.Next] {
			result.Valid = false
			result.Errors = append(result.Errors, ValidationError{
				Path:    path + ".next",
				Message: fmt.Sprintf("state '%s' does not exist", state.Next),
			})
		}

		for j, catchClause := range state.Catch {
			if catchClause.Next != "" && !stateNames[catchClause.Next] {
				result.Valid = false
				result.Errors = append(result.Errors, ValidationError{
					Path:    path + fmt.Sprintf(".catch[%d].next", j),
					Message: fmt.Sprintf("state '%s' does not exist", catchClause.Next),
				})
			}
		}
	}

	return result
}

// validateState validates a single state
func (v *Validator) validateState(state State, path string) []ValidationError {
	var errs []ValidationError

	switch state.Type {
	case StateTypeTask:
		if state.Resource == "" {
			errs = append(errs, ValidationError{
				Path:    path + ".resource",
				Message: "resource is required for Task state",
			})
		} else {
			// Check if activity exists in knowledge base
			if v.kb != nil && !v.kb.HasActivity(state.Resource) {
				errs = append(errs, ValidationError{
					Path:    path + ".resource",
					Message: fmt.Sprintf("activity '%s' not found in knowledge base", state.Resource),
				})
			}
		}

		// Validate timeout format
		if state.Timeout != "" {
			if err := validateDuration(state.Timeout); err != nil {
				errs = append(errs, ValidationError{
					Path:    path + ".timeout",
					Message: err.Error(),
				})
			}
		}

	case StateTypePass:
		// Pass state is valid with just a result
		if state.Result == nil {
			errs = append(errs, ValidationError{
				Path:    path + ".result",
				Message: "result is required for Pass state",
			})
		}

	case StateTypeFail:
		// Fail state requires error
		if state.Error == "" {
			errs = append(errs, ValidationError{
				Path:    path + ".error",
				Message: "error is required for Fail state",
			})
		}

	default:
		errs = append(errs, ValidationError{
			Path:    path + ".type",
			Message: fmt.Sprintf("invalid state type: %s", state.Type),
		})
	}

	return errs
}

// validateDuration validates Go duration string (e.g., "5m", "30s")
func validateDuration(dur string) error {
	_, err := time.ParseDuration(dur)
	if err != nil {
		return fmt.Errorf("invalid duration: %s", dur)
	}
	return nil
}

// String returns a human-readable validation result
func (vr *ValidationResult) String() string {
	if vr.Valid {
		return "Valid ✓"
	}

	msg := fmt.Sprintf("Invalid ✗ (%d errors):\n", len(vr.Errors))
	for _, err := range vr.Errors {
		msg += fmt.Sprintf("  %s: %s\n", err.Path, err.Message)
	}
	return msg
}
