package decision

import (
	"strings"
)

// Rule maps a condition to a set of actions.
type Rule struct {
	Name    string
	Match   MatchFunc
	Actions []Action
}

// MatchFunc is a pure predicate over a MessageContext.
type MatchFunc func(ctx MessageContext) bool

// Evaluate runs all rules against the context and returns the matching actions. Pure.
func Evaluate(ctx MessageContext, rules []Rule) []Action {
	var actions []Action
	for _, rule := range rules {
		if rule.Match(ctx) {
			actions = append(actions, rule.Actions...)
		}
	}
	return actions
}

// MatchCommand returns a MatchFunc that matches when the command equals cmd.
func MatchCommand(cmd string) MatchFunc {
	return func(ctx MessageContext) bool {
		return strings.EqualFold(ctx.Command, cmd)
	}
}

// MatchPrefix returns a MatchFunc that matches when content starts with prefix.
func MatchPrefix(prefix string) MatchFunc {
	return func(ctx MessageContext) bool {
		return strings.HasPrefix(ctx.Content, prefix)
	}
}

// MatchAny returns a MatchFunc that matches any message.
func MatchAny() MatchFunc {
	return func(_ MessageContext) bool { return true }
}

// MatchMinPowerLevel returns a MatchFunc that requires a minimum Matrix power level.
func MatchMinPowerLevel(level int) MatchFunc {
	return func(ctx MessageContext) bool {
		return ctx.PowerLevel >= level
	}
}

// And composes multiple MatchFuncs with logical AND.
func And(fns ...MatchFunc) MatchFunc {
	return func(ctx MessageContext) bool {
		for _, fn := range fns {
			if !fn(ctx) {
				return false
			}
		}
		return true
	}
}

// Or composes multiple MatchFuncs with logical OR.
func Or(fns ...MatchFunc) MatchFunc {
	return func(ctx MessageContext) bool {
		for _, fn := range fns {
			if fn(ctx) {
				return true
			}
		}
		return false
	}
}
