package routing

import (
	"context"
	"time"

	"go.uber.org/zap"
)

// Engine evaluates routing rules from the RuleCache against a RoutingContext.
// It is the central component of the intelligent routing system.
type Engine struct {
	cache  *RuleCache
	logger *zap.Logger
}

// New creates an Engine backed by store with the given cache TTL.
func New(store RuleStore, ttl time.Duration, logger *zap.Logger) *Engine {
	return &Engine{
		cache:  NewRuleCache(store, ttl, logger),
		logger: logger,
	}
}

// Start launches the cache background refresh goroutine.
func (e *Engine) Start() { e.cache.Start() }

// Stop shuts down the cache refresh goroutine.
func (e *Engine) Stop() { e.cache.Stop() }

// Cache returns the underlying RuleCache so callers (e.g. admin API) can
// invalidate entries after mutations.
func (e *Engine) Cache() *RuleCache { return e.cache }

// Evaluate finds the first matching rule for rctx and returns its Action.
// Rules are evaluated in priority order (ASC). All conditions within a rule
// must match (AND logic). An empty Conditions slice matches everything (catch-all).
//
// Returns (action, true, nil) on match.
// Returns (Action{}, false, nil) when no rule matches.
// Returns (Action{}, false, err) on cache/store errors.
func (e *Engine) Evaluate(ctx context.Context, rctx *RoutingContext) (Action, bool, error) {
	rules, err := e.cache.Get(ctx, rctx.TenantID)
	if err != nil {
		return Action{}, false, err
	}

	for _, rule := range rules { // already sorted ASC by priority
		if matchesAll(rule.Conditions, rctx) {
			e.logger.Debug("routing rule matched",
				zap.String("rule_id", rule.ID),
				zap.String("rule_name", rule.Name),
				zap.String("provider", rule.Action.Provider),
				zap.String("tenant_id", rctx.TenantID),
			)
			return rule.Action, true, nil
		}
	}
	return Action{}, false, nil
}

// matchesAll returns true if every condition in the slice holds for rctx.
// An empty slice is a catch-all — it always matches.
func matchesAll(conditions []Condition, rctx *RoutingContext) bool {
	for _, c := range conditions {
		if !c.Evaluate(rctx) {
			return false
		}
	}
	return true
}
