package com.xdap.self_development.job;

import com.xdap.self_development.common.BusinessDatabase;
import com.xdap.self_development.domain.pojo.ClassificationAttributePojo;
import com.xdap.self_development.domain.pojo.EnginePartsCollectionPojo;
import com.xdap.self_development.domain.view.CalculationParameterView;
import com.xdap.self_development.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ParameterValueCalculationTimer {
    @Resource
    private BusinessDatabase bd;

    private ScriptEngine scriptEngine;
    
    @PostConstruct
    public void init() {
        ScriptEngineManager scriptEngineManager;
        scriptEngineManager = new ScriptEngineManager();
        scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
    }

    public static Map<String, Map<String, String>> convertMap(
            List<CalculationParameterView> list
    ) {
        if (list == null) {
            return new HashMap<>();
        }

        return list.stream()
                .filter(Objects::nonNull)
                .filter(param -> param.getParameterValue() != null)
                .filter(param -> !"公式计算".equals(param.getParameterSource()))
                .filter(item -> item.getParameterName() != null && item.getModelId() != null)
                .collect(Collectors.groupingBy(
                        CalculationParameterView::getParameterName,
                        Collectors.toMap(
                                CalculationParameterView::getModelId,
                                item -> item.getParameterValue() != null ? item.getParameterValue() : item.getParameterName(),
                                (existing, replacement) -> replacement
                        )
                ));
    }

    /**
     * 定时任务：凌晨1点执行
     * 1. 查询来源为TC分类表且平台上为空的数据
     * 2. 从TC分类表更新数据到平台
     * 3. 执行公式计算
     * 4. 批量保存到数据库
     */
    @Scheduled(cron = "0 0 1 * * ?")
    @Transactional
    public void scheduledParameterCalculation() {
        log.info("========== 开始执行定时任务：参数值计算 ==========");
        log.info("执行时间：{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        
        try {
            // 步骤1：查询所有参数配置
            List<CalculationParameterView> parameterValues = bd.getBusinessDatabase()
                    .viewQueryMode(true)
                    .view("getModelParameterValue")
                    .doQuery(CalculationParameterView.class);
            
            log.info("共查询到{}个参数配置", parameterValues.size());
            
            if (parameterValues.isEmpty()) {
                log.info("无参数配置，跳过计算");
                return;
            }
            
            // 步骤2：从TC分类表更新参数值
            int tcUpdatedCount = updateParametersFromTC(parameterValues);
            log.info("TC分类表参数更新完成，成功更新{}个参数", tcUpdatedCount);
            
            // 步骤3：构建初始值映射
            Map<String, Map<String, String>> currentValues = convertMap(parameterValues);
            
            // 步骤4：执行公式计算
            List<CalculationParameterView> toUpdate = executeFormulaCalculation(parameterValues, currentValues);
            
            // 步骤5：批量更新数据库
            batchUpdateDatabase(toUpdate);
            
            log.info("========== 定时任务执行完成 ==========");
            
        } catch (Exception e) {
            log.error("定时任务执行失败：{}", e.getMessage(), e);
        }
    }
    
    /**
     * 从TC分类表更新参数值
     * 查询来源为TC分类表且平台上为空的数据
     */
    private int updateParametersFromTC(List<CalculationParameterView> parameterValues) {
        int updatedCount = 0;
        int skippedCount = 0;
        
        for (CalculationParameterView parameter : parameterValues) {
            if (!Objects.equals(parameter.getParameterSource(), "TC分类表")) {
                continue;
            }
            
            if (parameter.getParameterValue() != null && !parameter.getParameterValue().trim().isEmpty()) {
                continue;
            }
            
            try {
                EnginePartsCollectionPojo enginePartsCollection = bd.getTCDatabase()
                        .eq("ztj", parameter.getProductNumber())
                        .eq("lbjmc", parameter.getPartsName())
                        .doQueryFirst(EnginePartsCollectionPojo.class);
                
                if (enginePartsCollection == null || enginePartsCollection.getLbjth() == null) {
                    log.debug("机型【{}】的参数【{}】在TC分类表中未找到对应零部件，零部件名：{}",
                            parameter.getProductNumber(), parameter.getParameterName(), parameter.getPartsName());
                    skippedCount++;
                    continue;
                }
                
                ClassificationAttributePojo classificationAttribute = bd.getTCDatabase()
                        .eq("pitemId", enginePartsCollection.getLbjth())
                        .eq("idattrtbutehame", parameter.getParameterName())
                        .doQueryFirst(ClassificationAttributePojo.class);
                
                if (classificationAttribute != null && classificationAttribute.getFieldvalue() != null) {
                    parameter.setParameterValue(classificationAttribute.getFieldvalue());
                    log.info("机型【{}】的参数【{}】从TC更新值：{}", 
                            parameter.getProductNumber(), parameter.getParameterName(), classificationAttribute.getFieldvalue());
                    updatedCount++;
                } else {
                    log.debug("机型【{}】的参数【{}】在TC分类表中未找到对应值",
                            parameter.getProductNumber(), parameter.getParameterName());
                    skippedCount++;
                }
            } catch (Exception e) {
                log.error("机型【{}】的参数【{}】从TC更新失败：{}", 
                        parameter.getProductNumber(), parameter.getParameterName(), e.getMessage());
            }
        }
        
        log.info("TC参数处理完成：成功更新{}个，跳过{}个", updatedCount, skippedCount);
        return updatedCount;
    }
    
    /**
     * 执行公式计算
     */
    private List<CalculationParameterView> executeFormulaCalculation(
            List<CalculationParameterView> parameterValues,
            Map<String, Map<String, String>> currentValues) {
        
        List<CalculationParameterView> toUpdate = new ArrayList<>();
        
        List<CalculationParameterView> formulaParamsList = parameterValues.stream()
                .filter(p -> "公式计算".equals(p.getParameterSource()))
                .collect(Collectors.toList());
        
        log.info("开始执行公式计算，共{}个公式参数", formulaParamsList.size());
        
        int round = 0;
        final int MAX_ROUNDS = 5;
        boolean changed;
        Set<String> computedKeys = new HashSet<>();
        Map<String, String> errors = new HashMap<>();
        int successCount = 0;
        int failureCount = 0;
        
        do {
            changed = false;
            round++;
            
            for (CalculationParameterView param : formulaParamsList) {
                String modelId = param.getModelId();
                String paramName = param.getParameterName();
                String paramId = param.getParameterId();
                String key = modelId + "|" + paramId + "|" + paramName;
                
                if (computedKeys.contains(key)) {
                    continue;
                }
                
                try {
                    String replacedFormula = replaceParametersInFormula(
                            param.getNumberOrFormula(),
                            currentValues,
                            modelId,
                            paramName
                    );
                    
                    Double result = calculateExpression(replacedFormula);
                    if (result == null) {
                        errors.put(key, "替换公式：'" + replacedFormula + "'无法计算" + ",原公式为：'" + param.getNumberOrFormula() + "'");
                        continue;
                    }
                    String formattedResult = String.format("%.2f", result);
                    
                    currentValues.computeIfAbsent(paramName, k -> new HashMap<>()).put(modelId, formattedResult);
                    param.setParameterValue(formattedResult);
                    
                    computedKeys.add(key);
                    changed = true;
                    
                } catch (Exception e) {
                }
            }
            
        } while (changed && round < MAX_ROUNDS);
        
        for (CalculationParameterView param : formulaParamsList) {
            String key = param.getModelId() + "|" + param.getParameterId() + "|" + param.getParameterName();
            if (computedKeys.contains(key)) {
                successCount++;
                toUpdate.add(param);
            } else {
                String err = errors.get(key);
                if (err != null) {
                    log.error("机型【{}】的参数【{}】公式计算失败，{}",
                            param.getProductNumber(), param.getParameterName(), err);
                } else {
                    log.error("机型【{}】的参数【{}】公式计算失败",
                            param.getProductNumber(), param.getParameterName());
                }
                param.setParameterValue(null);
                toUpdate.add(param);
                failureCount++;
            }
        }
        
        log.info("公式计算完成：成功{}个，失败{}个", successCount, failureCount);
        return toUpdate;
    }
    
    /**
     * 批量更新数据库
     */
    private void batchUpdateDatabase(List<CalculationParameterView> toUpdate) {
        if (toUpdate.isEmpty()) {
            log.info("无需更新数据库");
            return;
        }
        
        int updatedCount = 0;
        int failureCount = 0;
        
        for (CalculationParameterView param : toUpdate) {
            try {
                if (param.getParameterValue() != null) {
                    bd.getBusinessDatabase()
                            .table("engine_param_detail")
                            .eq("id", param.getId())
                            .update("parameter_value", param.getParameterValue())
                            .doUpdate();
                    updatedCount++;
                }
            } catch (Exception e) {
                log.error("参数【{}】更新数据库失败：{}", param.getParameterName(), e.getMessage());
                failureCount++;
            }
        }
        
        log.info("数据库更新完成：成功{}个，失败{}个", updatedCount, failureCount);
    }

    // 每天凌晨执行一次
//    @Scheduled(cron = "0 0 0 * * ?")
    @Transactional
    public void dailyCalculate() {

        log.info("开始执行{}参数计算任务", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        // 统计信息
        int totalParameters;
        int tcParameters = 0;
        int calculatedParameters;
        int updatedFromTC = 0;
        int failedToUpdateFromTC = 0;
        int successCount = 0;
        int failureCount = 0;
        int updatedCount = 0;
        int dbUpdateFailures = 0;

        List<CalculationParameterView> toUpdate = new ArrayList<>();

        // 1.查询所有参数配置
        List<CalculationParameterView> parameterValues = bd.getBusinessDatabase()
                .viewQueryMode(true)
                .view("getModelParameterValue")
                .doQuery(CalculationParameterView.class);

        totalParameters = parameterValues.size();
        log.info("共查询到{}个参数配置", totalParameters);

        if (parameterValues.isEmpty()) {
            log.info("无参数配置，跳过计算");
        }

        // 2.从中台更新参数值
//        for (CalculationParameterView parameter : parameterValues) {
//            if (Objects.equals(parameter.getParameterSource(), "TC分类表")) {
//                tcParameters++;
//                try {
//                    EnginePartsCollectionPojo enginePartsCollection = bd.getTCDatabase()
//                            .eq("ztj", parameter.getProductNumber())
//                            .eq("lbjmc", parameter.getPartsName())
//                            .doQueryFirst(EnginePartsCollectionPojo.class);
//                    if (enginePartsCollection == null || enginePartsCollection.getLbjth() == null) {
//                        log.warn("机型【{}】的参数【{}】在TC分类表中未找到对应零部件，零部件名：{}",
//                                parameter.getProductNumber(), parameter.getParameterName(), parameter.getPartsName());
//                        failedToUpdateFromTC++;
//                        continue;
//                    }
//                    ClassificationAttributePojo classificationAttribute =  bd.getTCDatabase()
//                            .eq("pitemId", enginePartsCollection.getLbjth())
//                            .eq("idattrtbutehame", parameter.getParameterName())
//                            .doQueryFirst(ClassificationAttributePojo.class);
//                    if (classificationAttribute != null && classificationAttribute.getFieldvalue() != null) {
//                        String originalValue = parameter.getParameterValue();
//                        parameter.setParameterValue(classificationAttribute.getFieldvalue());
//                        log.debug("机型【{}】的参数【{}】从中台更新值：{} -> {}",parameter.getProductNumber(), parameter.getParameterName(), originalValue, classificationAttribute.getFieldvalue());
//                        updatedFromTC++;
//                    } else {
//                        log.warn("机型【{}】的参数【{}】在TC分类表中未找到对应值",parameter.getProductNumber(), parameter.getParameterName());
//                        failedToUpdateFromTC++;
//                    }
//                } catch (Exception e) {
//                    log.error("机型【{}】的参数【{}】从中台更新值失败：{}",parameter.getProductNumber(), parameter.getParameterName(), e.getMessage(), e);
//                    failedToUpdateFromTC++;
//                }
//            }
//        }

        log.info("从中台更新参数完成，共处理{}个TC参数，成功{}个，失败{}个", tcParameters, updatedFromTC, failedToUpdateFromTC);

        // 3.构建初始值映射 —— 仅包含非公式参数
        Map<String, Map<String, String>> currentValues = convertMap(parameterValues);

        // 4. 分离出所有"公式计算"类型的参数
        List<CalculationParameterView> formulaParamsList = parameterValues.stream()
                .filter(p -> "公式计算".equals(p.getParameterSource()))
                .collect(Collectors.toList());
        calculatedParameters = formulaParamsList.size();

        // 5. 多轮迭代计算
        int round = 0;
        final int MAX_ROUNDS = 5;
        boolean changed;
        Set<String> computedKeys = new HashSet<>();
        Map<String, String> errors = new HashMap<>();

        do {
            changed = false;
            round++;

            for (CalculationParameterView param : formulaParamsList) {
                String modelId = param.getModelId();
                String paramName = param.getParameterName();
                String paramId = param.getParameterId();
                String key = modelId + "|" + paramId + "|" + paramName;

                // 跳过已成功计算的
                if (computedKeys.contains(key)) {
                    continue;
                }

                try {
                    String replacedFormula = replaceParametersInFormula(
                            param.getNumberOrFormula(),
                            currentValues,   // 只包含原始输入 + 已算出的公式值
                            modelId,
                            paramName
                    );

                    Double result = calculateExpression(replacedFormula);
                    if (result == null) {
                        errors.put(key, "替换公式：'" + replacedFormula + "'无法计算" + ",原公式为：'" + param.getNumberOrFormula() + "'");
                        continue;
                    }
                    String formattedResult = String.format("%.2f", result);

                    // 更新内存中的值
                    currentValues.computeIfAbsent(paramName, k -> new HashMap<>()).put(modelId, formattedResult);
                    param.setParameterValue(formattedResult);

                    computedKeys.add(key);
                    changed = true;

                } catch (Exception e) {
                    // 暂不报错，留到最后一轮统一处理
                }
            }

        } while (changed && round < MAX_ROUNDS);

        // 6. 标记最终失败的参数
        for (CalculationParameterView param : formulaParamsList) {
            String key = param.getModelId() + "|" + param.getParameterId() + "|" + param.getParameterName();
            if (computedKeys.contains(key)) {
                successCount++;
                toUpdate.add(param);
            } else {
                String err = errors.get(key);
                if (err != null) {
                    log.error("机型【{}】的参数【{}】最终计算失败，{}",
                            param.getProductNumber(), param.getParameterName(), err);
                } else {
                    log.error("机型【{}】的参数【{}】最终计算失败，无具体错误信息",
                            param.getProductNumber(), param.getParameterName());
                }
                param.setParameterValue(null);
                toUpdate.add(param);
                failureCount++;
            }
        }

        log.info("公式计算参数完成，共处理{}个计算参数，成功{}个，失败{}个", calculatedParameters, successCount, failureCount);

        // 7. 批量更新数据库
        if (!toUpdate.isEmpty()) {
            for (CalculationParameterView param : toUpdate) {
                try {
                    if (param.getParameterValue() != null) {
                        bd.getBusinessDatabase()
                                .table("engine_param_detail")
                                .eq("id", param.getId())
                                .update("parameter_value", param.getParameterValue())
                                .doUpdate();
                    }
                    updatedCount++;
                } catch (Exception e) {
                    log.error("参数【{}】更新数据库失败：{}", param.getParameterName(), e.getMessage(), e);
                    dbUpdateFailures++;
                }
            }
            log.info("{}参数计算完成，成功处理{}个参数，失败{}个参数，更新{}个参数，数据库更新失败{}个",
                    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), successCount, failureCount, updatedCount, dbUpdateFailures);
        } else {
            log.info("{}参数计算完成，无需更新任何参数", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }

        log.info("{}参数计算任务结束，总参数数{}，TC参数{}(成功{}，失败{})，计算参数{}(成功{}，失败{})，数据库更新{}(失败{})",
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), totalParameters, tcParameters, updatedFromTC, failedToUpdateFromTC,
                calculatedParameters, successCount, failureCount, updatedCount, dbUpdateFailures);
    }

    private String replaceParametersInFormula(String formula,
                                              Map<String, Map<String, String>> currentValueMap,
                                              String modelId,
                                              String currentParamName) {
        String result = formula.replace("π", "Math.PI");

        // 获取所有已知参数名，按长度降序
        List<String> knownParams = currentValueMap.keySet().stream()
                .filter(name -> !Objects.equals(name, currentParamName))
                .sorted((a, b) -> Integer.compare(b.length(), a.length()))
                .collect(Collectors.toList());

        for (String paramName : knownParams) {
            String value = getParameterValue(currentValueMap, paramName, modelId);
            // 仅当 value 是有效数字时才替换
            if (value != null && !value.equals(paramName) && isNumeric(value)) {
                String escaped = Pattern.quote(paramName);
                // 使用否定前后瞻，支持中文和中文括号
                String pattern = "(?<![\u4e00-\u9fa5a-zA-Z0-9（）])" + escaped + "(?![\u4e00-\u9fa5a-zA-Z0-9（）])";
                result = result.replaceAll(pattern, value);
            }
        }

        return result;
    }

    private String getParameterValue(Map<String, Map<String, String>> valueMap, String paramName, String modelId) {
        Map<String, String> modelValues = valueMap.get(paramName);
        if (modelValues != null) {
            String value = modelValues.get(modelId);
            if (value != null && !value.trim().isEmpty()) {
                return value;
            }
        }
        return paramName; // 找不到返回原名（会导致非数字）
    }

    private boolean isNumeric(String str) {
        if (str == null || str.isEmpty()) {
            return false;
        }
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    private boolean isSafeExpression(String expression) {
        // 允许的字符：数字、基本运算符、括号、数学常量和函数
        String safePattern = "^[0-9+\\-*/().\\sMathPIEsincostanlogsqrtabs]+$";

        if (!expression.matches(safePattern)) {
            return false;
        }
        String[] dangerousKeywords = {"java", "class", "runtime", "exec", "system", "process", "file", "import"};
        for (String keyword : dangerousKeywords) {
            if (expression.toLowerCase().contains(keyword)) {
                return false;
            }
        }
        return true;
    }

    private Double calculateExpression(String expression) throws CommonException {
        try {
            if (!isSafeExpression(expression)) {
                return null;
            }
            Object result = scriptEngine.eval(expression);
            if (result instanceof Number) {
                return ((Number) result).doubleValue();
            }
            return null;
        } catch (Exception e) {
            log.warn("{}导致计算失败: {}", e.getMessage(), expression, e);
            return null;
        }
    }
}
