package routing

import (
	"fmt"
)

// SpecBuilder defines interface for building workflow specs
type SpecBuilder interface {
	// FromIntent builds spec from an analyzed intent
	FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error)
	
	// Validate checks if builder can build this intent
	Validate(intent *Intent) error
}

// BuildMetadata contains metadata for spec building
type BuildMetadata struct {
	KnowledgeBase *KnowledgeBase
	Context       map[string]interface{}
	Validators    []WorkflowValidator
}

// DefaultSpecBuilder implements basic spec building
type DefaultSpecBuilder struct {
	kb *KnowledgeBase
}

// NewDefaultSpecBuilder creates a builder
func NewDefaultSpecBuilder(kb *KnowledgeBase) *DefaultSpecBuilder {
	return &DefaultSpecBuilder{kb: kb}
}

// Validate checks if intent is buildable
func (dsb *DefaultSpecBuilder) Validate(intent *Intent) error {
	if len(intent.Activities) == 0 {
		return fmt.Errorf("intent has no activities")
	}
	
	for _, actName := range intent.Activities {
		if !dsb.kb.HasActivity(actName) {
			return fmt.Errorf("activity %s not found", actName)
		}
	}
	
	return nil
}

// FromIntent builds spec from intent
func (dsb *DefaultSpecBuilder) FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) {
	if err := dsb.Validate(intent); err != nil {
		return nil, err
	}
	
	states := make([]State, 0, len(intent.Activities)+1)
	
	// Build states for each activity
	for i, actName := range intent.Activities {
		act := dsb.kb.GetActivity(actName)
		
		state := State{
			Name:       actName,
			Type:       StateTypeTask,
			Resource:   actName,
			Parameters: dsb.buildParameters(act, intent, i),
			Timeout:    act.Constraints.DefaultTimeout,
			Retry:      dsb.buildRetryPolicy(act, intent),
		}
		
		// Set next state
		if i < len(intent.Activities)-1 {
			state.Next = intent.Activities[i+1]
		} else {
			state.End = true
		}
		
		// Add catch for flaky activities
		if act.Constraints.IsFlaky && intent.ErrorHandling != "fail-fast" {
			state.Catch = []CatchClause{
				{
					ErrorEquals: []string{"ActivityError", "TimeoutError"},
					Next:        "HandleError",
				},
			}
		}
		
		states = append(states, state)
	}
	
	// Add error handler if needed
	if dsb.hasFlaky(intent) && intent.ErrorHandling != "fail-fast" {
		states = append(states, State{
			Name:  "HandleError",
			Type:  StateTypeFail,
			Error: "ActivityFailed",
			Cause: "One or more activities failed",
		})
	}
	
	// Build input map
	inputMap := make(map[string]interface{})
	for k, v := range intent.Parameters {
		inputMap[k] = v
	}
	if metadata != nil && metadata.Context != nil {
		for k, v := range metadata.Context {
			if _, exists := inputMap[k]; !exists {
				inputMap[k] = v
			}
		}
	}
	
	spec := &WorkflowSpec{
		Name:   intent.WorkflowName,
		Input:  inputMap,
		States: states,
	}
	
	// Validate if validators provided
	if metadata != nil && len(metadata.Validators) > 0 {
		for _, v := range metadata.Validators {
			if err := v.Validate(spec); err != nil {
				return nil, err
			}
		}
	}
	
	return spec, nil
}

// buildParameters creates parameters for activity
func (dsb *DefaultSpecBuilder) buildParameters(act *ActivityMetadata, intent *Intent, stateIndex int) map[string]interface{} {
	params := make(map[string]interface{})
	resolver := &paramResolver{
		intent: intent,
		kb:     dsb.kb,
	}
	
	if stateIndex > 0 {
		resolver.prevState = intent.Activities[stateIndex-1]
	}
	
	for name, def := range act.Inputs {
		if val := resolver.resolve(name, def); val != nil {
			params[name] = val
		}
	}
	
	return params
}

// buildRetryPolicy creates retry policy
func (dsb *DefaultSpecBuilder) buildRetryPolicy(act *ActivityMetadata, intent *Intent) *RetryPolicy {
	if intent.ErrorHandling == "fail-fast" {
		return &RetryPolicy{
			MaxAttempts:     1,
			BackoffRate:     1.0,
			InitialInterval: "1s",
		}
	}
	
	return &RetryPolicy{
		MaxAttempts:     int32(act.Constraints.RecommendedRetries),
		BackoffRate:     act.Constraints.RetryBackoff,
		InitialInterval: "1s",
		MaxInterval:     "30s",
	}
}

// hasFlaky checks if any activity is flaky
func (dsb *DefaultSpecBuilder) hasFlaky(intent *Intent) bool {
	for _, actName := range intent.Activities {
		act := dsb.kb.GetActivity(actName)
		if act != nil && act.Constraints.IsFlaky {
			return true
		}
	}
	return false
}

// CronSpecBuilder builds cron workflow specs
type CronSpecBuilder struct {
	regularBuilder SpecBuilder
}

// NewCronSpecBuilder creates a cron builder
func NewCronSpecBuilder(regularBuilder SpecBuilder) *CronSpecBuilder {
	return &CronSpecBuilder{
		regularBuilder: regularBuilder,
	}
}

// Validate checks if intent is valid for cron
func (csb *CronSpecBuilder) Validate(intent *Intent) error {
	if !intent.IsCron {
		return fmt.Errorf("intent is not marked as cron")
	}
	
	if intent.CronSchedule == "" {
		return fmt.Errorf("cron schedule is empty")
	}
	
	return csb.regularBuilder.Validate(intent)
}

// FromIntent builds cron spec
func (csb *CronSpecBuilder) FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) {
	spec, err := csb.regularBuilder.FromIntent(intent, metadata)
	if err != nil {
		return nil, err
	}
	
	// In production, wrap with cron metadata
	// For now, just return the regular spec
	return spec, nil
}

// SpecBuilderFactory creates appropriate spec builders
type SpecBuilderFactory struct {
	kb *KnowledgeBase
}

// NewSpecBuilderFactory creates a factory
func NewSpecBuilderFactory(kb *KnowledgeBase) *SpecBuilderFactory {
	return &SpecBuilderFactory{kb: kb}
}

// CreateBuilder creates appropriate builder for intent
func (sbf *SpecBuilderFactory) CreateBuilder(intent *Intent) (SpecBuilder, error) {
	if intent.IsCron {
		return NewCronSpecBuilder(NewDefaultSpecBuilder(sbf.kb)), nil
	}
	
	return NewDefaultSpecBuilder(sbf.kb), nil
}

// CompositeSpecBuilder combines multiple builders with fallback
type CompositeSpecBuilder struct {
	builders []SpecBuilder
}

// NewCompositeSpecBuilder creates a composite builder
func NewCompositeSpecBuilder(builders ...SpecBuilder) *CompositeSpecBuilder {
	return &CompositeSpecBuilder{builders: builders}
}

// Validate tries each builder
func (csb *CompositeSpecBuilder) Validate(intent *Intent) error {
	var lastErr error
	
	for _, builder := range csb.builders {
		if err := builder.Validate(intent); err == nil {
			return nil
		} else {
			lastErr = err
		}
	}
	
	if lastErr != nil {
		return lastErr
	}
	return fmt.Errorf("no builder could validate intent")
}

// FromIntent tries each builder
func (csb *CompositeSpecBuilder) FromIntent(intent *Intent, metadata *BuildMetadata) (*WorkflowSpec, error) {
	var lastErr error
	
	for _, builder := range csb.builders {
		if spec, err := builder.FromIntent(intent, metadata); err == nil {
			return spec, nil
		} else {
			lastErr = err
		}
	}
	
	if lastErr != nil {
		return nil, lastErr
	}
	return nil, fmt.Errorf("no builder could create spec")
}
