package bandit

import (
	"strconv"
	"strings"

	"github.com/operator/command-center/internal/webhooks"
)

// evalPredicate runs a tiny expression language against the grab's
// attributes. The grammar is intentionally narrow:
//
//   - "true" / "false"
//   - <field> <op> <value>     for op ∈ { ==, !=, <=, >=, <, > }
//   - <expr> && <expr>
//   - <expr> || <expr>
//
// Fields: size_bytes, release_name (string), filter_name, indexer.
// String comparison uses == / != with quoted literals.
func evalPredicate(expr string, g webhooks.AutobrrGrabEvent) bool {
	expr = strings.TrimSpace(expr)
	if expr == "" {
		return true
	}
	if expr == "true" {
		return true
	}
	if expr == "false" {
		return false
	}
	// Top-level || splits.
	if ors := splitTop(expr, "||"); len(ors) > 1 {
		for _, p := range ors {
			if evalPredicate(p, g) {
				return true
			}
		}
		return false
	}
	// Then top-level &&.
	if ands := splitTop(expr, "&&"); len(ands) > 1 {
		for _, p := range ands {
			if !evalPredicate(p, g) {
				return false
			}
		}
		return true
	}
	return evalComparison(expr, g)
}

func splitTop(s, sep string) []string {
	parts := []string{}
	depth := 0
	current := strings.Builder{}
	i := 0
	for i < len(s) {
		if depth == 0 && i+len(sep) <= len(s) && s[i:i+len(sep)] == sep {
			parts = append(parts, current.String())
			current.Reset()
			i += len(sep)
			continue
		}
		ch := s[i]
		if ch == '(' {
			depth++
		} else if ch == ')' {
			depth--
		}
		current.WriteByte(ch)
		i++
	}
	parts = append(parts, current.String())
	return parts
}

func evalComparison(expr string, g webhooks.AutobrrGrabEvent) bool {
	expr = strings.TrimSpace(expr)
	for _, op := range []string{"==", "!=", "<=", ">=", "<", ">"} {
		idx := strings.Index(expr, op)
		if idx < 0 {
			continue
		}
		lhs := strings.TrimSpace(expr[:idx])
		rhs := strings.TrimSpace(expr[idx+len(op):])
		return compare(lhs, op, rhs, g)
	}
	return false
}

func compare(lhs, op, rhs string, g webhooks.AutobrrGrabEvent) bool {
	lv := fieldVal(lhs, g)
	rv := strings.Trim(rhs, " '\"")
	// numeric comparison if both look numeric.
	if ln, lerr := toFloat(lv); lerr == nil {
		if rn, rerr := toFloat(rv); rerr == nil {
			switch op {
			case "==":
				return ln == rn
			case "!=":
				return ln != rn
			case "<":
				return ln < rn
			case "<=":
				return ln <= rn
			case ">":
				return ln > rn
			case ">=":
				return ln >= rn
			}
		}
	}
	// fall back to string comparison.
	switch op {
	case "==":
		return lv == rv
	case "!=":
		return lv != rv
	}
	return false
}

func fieldVal(name string, g webhooks.AutobrrGrabEvent) string {
	switch strings.TrimSpace(name) {
	case "size_bytes":
		return strconv.FormatInt(g.Size, 10)
	case "release_name":
		return g.ReleaseName
	case "filter_name":
		return g.FilterName
	case "indexer":
		return g.Indexer
	case "filter_id":
		return g.FilterID
	}
	return ""
}

func toFloat(s string) (float64, error) {
	return strconv.ParseFloat(strings.TrimSpace(s), 64)
}
