package com.yhy.module.core.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * 公式计算工具类
 * 支持四则运算、括号、优先级
 * 
 * @author yihuiyong
 */
public class FormulaEvaluator {

    /**
     * 计算公式
     * 
     * @param formula 公式字符串，如 "(人+机)-(材*0.3)"
     * @param variables 变量映射表，key为变量名，value为变量值
     * @return 计算结果
     * @throws IllegalArgumentException 公式解析失败
     */
    public static BigDecimal evaluate(String formula, Map<String, BigDecimal> variables) {
        if (formula == null || formula.trim().isEmpty()) {
            throw new IllegalArgumentException("公式不能为空");
        }
        
        // 替换变量
        String expression = replaceVariables(formula, variables);
        
        // 计算表达式
        return calculateExpression(expression);
    }

    /**
     * 替换公式中的变量
     */
    private static String replaceVariables(String formula, Map<String, BigDecimal> variables) {
        // 按变量名长度倒序排序，避免短变量名替换长变量名的问题
        // 例如：先替换"人机"再替换"人"
        List<Map.Entry<String, BigDecimal>> sortedEntries = variables.entrySet().stream()
            .sorted((e1, e2) -> Integer.compare(e2.getKey().length(), e1.getKey().length()))
            .collect(Collectors.toList());
        
        String result = formula;
        for (Map.Entry<String, BigDecimal> entry : sortedEntries) {
            String varName = entry.getKey();
            BigDecimal value = entry.getValue();
            result = result.replace(varName, value.toString());
        }
        
        return result;
    }

    /**
     * 计算表达式（支持括号和四则运算）
     */
    private static BigDecimal calculateExpression(String expression) {
        // 去除空格
        expression = expression.replaceAll("\\s+", "");
        
        // 验证括号匹配
        if (!isValidParentheses(expression)) {
            throw new IllegalArgumentException("括号不匹配");
        }
        
        // 使用双栈算法计算表达式
        return evaluateWithStacks(expression);
    }

    /**
     * 验证括号是否匹配
     */
    private static boolean isValidParentheses(String expression) {
        int count = 0;
        for (char c : expression.toCharArray()) {
            if (c == '(') {
                count++;
            } else if (c == ')') {
                count--;
                if (count < 0) {
                    return false;
                }
            }
        }
        return count == 0;
    }

    /**
     * 使用双栈算法计算表达式
     * 一个栈存储数字，一个栈存储运算符
     */
    private static BigDecimal evaluateWithStacks(String expression) {
        Stack<BigDecimal> numbers = new Stack<>();
        Stack<Character> operators = new Stack<>();
        
        int i = 0;
        while (i < expression.length()) {
            char c = expression.charAt(i);
            
            // 跳过空格
            if (c == ' ') {
                i++;
                continue;
            }
            
            // 处理数字（包括小数和科学计数法，如 1.5E-5）
            if (Character.isDigit(c) || c == '.') {
                StringBuilder sb = new StringBuilder();
                while (i < expression.length()) {
                    char ch = expression.charAt(i);
                    // 支持数字、小数点、科学计数法（E/e）及其后的正负号
                    if (Character.isDigit(ch) || ch == '.') {
                        sb.append(ch);
                        i++;
                    } else if ((ch == 'E' || ch == 'e') && sb.length() > 0) {
                        // 科学计数法
                        sb.append(ch);
                        i++;
                        // 处理指数部分的正负号
                        if (i < expression.length() && (expression.charAt(i) == '+' || expression.charAt(i) == '-')) {
                            sb.append(expression.charAt(i));
                            i++;
                        }
                    } else {
                        break;
                    }
                }
                numbers.push(new BigDecimal(sb.toString()));
                continue;
            }
            
            // 处理左括号
            if (c == '(') {
                operators.push(c);
                i++;
                continue;
            }
            
            // 处理右括号
            if (c == ')') {
                while (!operators.isEmpty() && operators.peek() != '(') {
                    numbers.push(applyOperator(operators.pop(), numbers.pop(), numbers.pop()));
                }
                operators.pop(); // 弹出左括号
                i++;
                continue;
            }
            
            // 处理运算符
            if (isOperator(c)) {
                // 处理负号
                if (c == '-' && (i == 0 || expression.charAt(i - 1) == '(' || isOperator(expression.charAt(i - 1)))) {
                    // 负号：将其视为 0 - number
                    numbers.push(BigDecimal.ZERO);
                }
                
                while (!operators.isEmpty() && hasPrecedence(c, operators.peek())) {
                    numbers.push(applyOperator(operators.pop(), numbers.pop(), numbers.pop()));
                }
                operators.push(c);
                i++;
                continue;
            }
            
            throw new IllegalArgumentException("无效字符: " + c);
        }
        
        // 处理剩余的运算符
        while (!operators.isEmpty()) {
            numbers.push(applyOperator(operators.pop(), numbers.pop(), numbers.pop()));
        }
        
        return numbers.pop();
    }

    /**
     * 判断是否为运算符
     */
    private static boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/';
    }

    /**
     * 判断运算符优先级
     * 
     * @param op1 当前运算符
     * @param op2 栈顶运算符
     * @return true 如果栈顶运算符优先级 >= 当前运算符
     */
    private static boolean hasPrecedence(char op1, char op2) {
        if (op2 == '(' || op2 == ')') {
            return false;
        }
        if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) {
            return false;
        }
        return true;
    }

    /**
     * 应用运算符
     * 
     * @param operator 运算符
     * @param b 第二个操作数
     * @param a 第一个操作数
     * @return 计算结果
     */
    private static BigDecimal applyOperator(char operator, BigDecimal b, BigDecimal a) {
        switch (operator) {
            case '+':
                return a.add(b);
            case '-':
                return a.subtract(b);
            case '*':
                return a.multiply(b);
            case '/':
                if (b.compareTo(BigDecimal.ZERO) == 0) {
                    throw new ArithmeticException("除数不能为零");
                }
                return a.divide(b, 4, RoundingMode.HALF_UP);
            default:
                throw new IllegalArgumentException("无效运算符: " + operator);
        }
    }
}
