package rule

import (
	"github.com/expr-lang/expr"
	"github.com/expr-lang/expr/vm"
	"github.com/pkg/errors"
)

type Engine[E any] struct {
	rules []*vm.Program
}

func (e *Engine[E]) Apply(env E) ([]any, error) {
	results := make([]any, 0, len(e.rules))
	for i, r := range e.rules {
		result, err := expr.Run(r, env)
		if err != nil {
			return nil, errors.Wrapf(err, "could not run rule #%d", i)
		}

		results = append(results, result)
	}

	return results, nil
}

func NewEngine[E any](funcs ...OptionFunc) (*Engine[E], error) {
	opts := NewOptions(funcs...)

	engine := &Engine[E]{
		rules: make([]*vm.Program, 0, len(opts.Rules)),
	}

	for i, r := range opts.Rules {
		program, err := expr.Compile(r, opts.Expr...)
		if err != nil {
			return nil, errors.Wrapf(err, "could not compile rule #%d", i)
		}

		engine.rules = append(engine.rules, program)
	}

	return engine, nil
}