package workflow

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

var expressionOps = []string{">=", "<=", "==", "!=", " contains ", " matches ", ">", "<"}
var jsonFuncRe = regexp.MustCompile(`^(jsonpath|jq)\((.*),\s*(['"][^'"]+['"])\)$`)
var jsonFuncFindRe = regexp.MustCompile(`(jsonpath|jq)\([^)]*\)`)
var singleTemplateVarRe = regexp.MustCompile(`^\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}$`)

func validateConditionExpression(expr string) error {
	expr = strings.TrimSpace(expr)
	if expr == "" {
		return fmt.Errorf("条件表达式不能为空")
	}
	for _, part := range splitBoolExpr(expr, "||") {
		for _, atom := range splitBoolExpr(part, "&&") {
			if err := validateConditionAtom(atom); err != nil {
				return err
			}
		}
	}
	return nil
}

func validateConditionAtom(expr string) error {
	expr = strings.TrimSpace(expr)
	if expr == "" {
		return fmt.Errorf("条件表达式存在空片段")
	}
	if strings.Count(expr, "{{") != strings.Count(expr, "}}") {
		return fmt.Errorf("条件表达式模板括号不匹配: %s", expr)
	}
	if err := validateJSONFunctions(expr); err != nil {
		return err
	}
	if left, right, ok := splitExpressionAtom(expr, " matches "); ok {
		if strings.TrimSpace(left) == "" || strings.TrimSpace(right) == "" {
			return fmt.Errorf("matches 表达式两侧不能为空: %s", expr)
		}
		pattern := cleanComparable(resolveStaticTemplate(right))
		if _, err := regexp.Compile(pattern); err != nil {
			return fmt.Errorf("matches 正则非法: %w", err)
		}
		return nil
	}
	for _, op := range expressionOps {
		if op == " matches " {
			continue
		}
		if left, right, ok := splitExpressionAtom(expr, op); ok {
			if strings.TrimSpace(left) == "" || strings.TrimSpace(right) == "" {
				return fmt.Errorf("表达式 %q 两侧不能为空: %s", strings.TrimSpace(op), expr)
			}
			return nil
		}
	}
	return nil
}

func evalCondition(expr string, state *WorkflowLocalState) bool {
	expr = strings.TrimSpace(expr)
	if expr == "" {
		return true
	}
	orParts := splitBoolExpr(expr, "||")
	for _, orPart := range orParts {
		andOK := true
		for _, atom := range splitBoolExpr(orPart, "&&") {
			if !evalConditionAtom(atom, state) {
				andOK = false
				break
			}
		}
		if andOK {
			return true
		}
	}
	return false
}

func evalConditionAtom(expr string, state *WorkflowLocalState) bool {
	expr = strings.TrimSpace(expr)
	for _, op := range expressionOps {
		if left, right, ok := splitExpressionAtom(expr, op); ok {
			left = strings.TrimSpace(fmt.Sprint(resolveExpressionOperand(left, state)))
			right = strings.TrimSpace(fmt.Sprint(resolveExpressionOperand(right, state)))
			switch strings.TrimSpace(op) {
			case "==":
				return cleanComparable(left) == cleanComparable(right)
			case "!=":
				return cleanComparable(left) != cleanComparable(right)
			case ">":
				return compareNumeric(left, right, func(a, b float64) bool { return a > b })
			case ">=":
				return compareNumeric(left, right, func(a, b float64) bool { return a >= b })
			case "<":
				return compareNumeric(left, right, func(a, b float64) bool { return a < b })
			case "<=":
				return compareNumeric(left, right, func(a, b float64) bool { return a <= b })
			case "contains":
				return strings.Contains(cleanComparable(left), cleanComparable(right))
			case "matches":
				matched, _ := regexp.MatchString(cleanComparable(right), cleanComparable(left))
				return matched
			}
		}
	}
	resolved := strings.TrimSpace(fmt.Sprint(resolveExpressionOperand(expr, state)))
	v := strings.ToLower(cleanComparable(resolved))
	return v != "" && v != "false" && v != "0" && v != "null"
}

func splitBoolExpr(expr, sep string) []string {
	parts := strings.Split(expr, sep)
	out := make([]string, 0, len(parts))
	for _, part := range parts {
		if s := strings.TrimSpace(part); s != "" {
			out = append(out, s)
		}
	}
	if len(out) == 0 {
		return []string{strings.TrimSpace(expr)}
	}
	return out
}

func splitExpressionAtom(expr, op string) (string, string, bool) {
	if strings.TrimSpace(op) == "contains" || strings.TrimSpace(op) == "matches" {
		idx := strings.Index(expr, op)
		if idx < 0 {
			return "", "", false
		}
		return expr[:idx], expr[idx+len(op):], true
	}
	idx := strings.Index(expr, op)
	if idx < 0 {
		return "", "", false
	}
	return expr[:idx], expr[idx+len(op):], true
}

func compareNumeric(left, right string, cmp func(float64, float64) bool) bool {
	a, errA := strconv.ParseFloat(cleanComparable(left), 64)
	b, errB := strconv.ParseFloat(cleanComparable(right), 64)
	if errA != nil || errB != nil {
		return false
	}
	return cmp(a, b)
}

func resolveStaticTemplate(s string) string {
	return templateVarRe.ReplaceAllString(s, "value")
}

func resolveExpressionOperand(raw string, state *WorkflowLocalState) any {
	raw = strings.TrimSpace(raw)
	if m := jsonFuncRe.FindStringSubmatch(raw); len(m) == 4 {
		inputExpr := strings.TrimSpace(m[2])
		path := strings.Trim(m[3], `"'`)
		input := resolveExpressionOperand(inputExpr, state)
		return evalJSONPathValue(input, path)
	}
	if m := singleTemplateVarRe.FindStringSubmatch(raw); len(m) == 2 {
		return valueFromPath(m[1], state)
	}
	return resolveTemplate(raw, state)
}

func validateJSONFunctions(expr string) error {
	for _, candidate := range jsonFuncFindRe.FindAllString(expr, -1) {
		candidate = strings.TrimSpace(candidate)
		m := jsonFuncRe.FindStringSubmatch(candidate)
		if len(m) != 4 {
			return fmt.Errorf("JSONPath/JQ 函数格式应为 jsonpath(value, \"$.path\") 或 jq(value, \".path\")")
		}
		if err := validateJSONPathSyntax(strings.Trim(m[3], `"'`)); err != nil {
			return err
		}
	}
	return nil
}
