package com.yhy.module.core.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;

/**
 * 工料机价格计算工具类
 * 后台（QuotaResourceServiceImpl）和工作台（WbBoqResourceServiceImpl）共用此逻辑
 * 
 * 设计模式：函数式工具类 + FieldAccessor Builder
 * 与 AdjustmentFormulaCalculator 保持一致的设计风格
 *
 * @author yhy
 */
@Slf4j
public class ResourcePriceCalculator {

    /**
     * 字段访问器，用于泛型访问不同 VO 类型的相同语义字段
     * 后台 VO 字段名：resourceUnit / resourceCategoryId / dosage / adjustedDosage
     * 工作台 VO 字段名：unit / categoryId / consumeQty / adjustConsumeQty
     */
    public static class FieldAccessor<T> {
        private final Function<T, String> getUnit;
        private final Function<T, Long> getCategoryId;
        private final Function<T, BigDecimal> getEffectiveDosage;
        private final Function<T, Map<String, Object>> getCalcBase;
        private final Function<T, BigDecimal> getTaxExclBaseTotalSum;
        private final Function<T, BigDecimal> getTaxInclBaseTotalSum;
        private final Function<T, BigDecimal> getTaxExclCompileTotalSum;
        private final Function<T, BigDecimal> getTaxInclCompileTotalSum;
        private final BiConsumer<T, BigDecimal> setTaxExclBaseTotalSum;
        private final BiConsumer<T, BigDecimal> setTaxInclBaseTotalSum;
        private final BiConsumer<T, BigDecimal> setTaxExclCompileTotalSum;
        private final BiConsumer<T, BigDecimal> setTaxInclCompileTotalSum;

        private FieldAccessor(Builder<T> builder) {
            this.getUnit = builder.getUnit;
            this.getCategoryId = builder.getCategoryId;
            this.getEffectiveDosage = builder.getEffectiveDosage;
            this.getCalcBase = builder.getCalcBase;
            this.getTaxExclBaseTotalSum = builder.getTaxExclBaseTotalSum;
            this.getTaxInclBaseTotalSum = builder.getTaxInclBaseTotalSum;
            this.getTaxExclCompileTotalSum = builder.getTaxExclCompileTotalSum;
            this.getTaxInclCompileTotalSum = builder.getTaxInclCompileTotalSum;
            this.setTaxExclBaseTotalSum = builder.setTaxExclBaseTotalSum;
            this.setTaxInclBaseTotalSum = builder.setTaxInclBaseTotalSum;
            this.setTaxExclCompileTotalSum = builder.setTaxExclCompileTotalSum;
            this.setTaxInclCompileTotalSum = builder.setTaxInclCompileTotalSum;
        }

        public static <T> Builder<T> builder() {
            return new Builder<>();
        }

        public static class Builder<T> {
            private Function<T, String> getUnit;
            private Function<T, Long> getCategoryId;
            private Function<T, BigDecimal> getEffectiveDosage;
            private Function<T, Map<String, Object>> getCalcBase;
            private Function<T, BigDecimal> getTaxExclBaseTotalSum;
            private Function<T, BigDecimal> getTaxInclBaseTotalSum;
            private Function<T, BigDecimal> getTaxExclCompileTotalSum;
            private Function<T, BigDecimal> getTaxInclCompileTotalSum;
            private BiConsumer<T, BigDecimal> setTaxExclBaseTotalSum;
            private BiConsumer<T, BigDecimal> setTaxInclBaseTotalSum;
            private BiConsumer<T, BigDecimal> setTaxExclCompileTotalSum;
            private BiConsumer<T, BigDecimal> setTaxInclCompileTotalSum;

            public Builder<T> getUnit(Function<T, String> getUnit) {
                this.getUnit = getUnit;
                return this;
            }

            public Builder<T> getCategoryId(Function<T, Long> getCategoryId) {
                this.getCategoryId = getCategoryId;
                return this;
            }

            public Builder<T> getEffectiveDosage(Function<T, BigDecimal> getEffectiveDosage) {
                this.getEffectiveDosage = getEffectiveDosage;
                return this;
            }

            public Builder<T> getCalcBase(Function<T, Map<String, Object>> getCalcBase) {
                this.getCalcBase = getCalcBase;
                return this;
            }

            public Builder<T> getTaxExclBaseTotalSum(Function<T, BigDecimal> getTaxExclBaseTotalSum) {
                this.getTaxExclBaseTotalSum = getTaxExclBaseTotalSum;
                return this;
            }

            public Builder<T> getTaxInclBaseTotalSum(Function<T, BigDecimal> getTaxInclBaseTotalSum) {
                this.getTaxInclBaseTotalSum = getTaxInclBaseTotalSum;
                return this;
            }

            public Builder<T> getTaxExclCompileTotalSum(Function<T, BigDecimal> getTaxExclCompileTotalSum) {
                this.getTaxExclCompileTotalSum = getTaxExclCompileTotalSum;
                return this;
            }

            public Builder<T> getTaxInclCompileTotalSum(Function<T, BigDecimal> getTaxInclCompileTotalSum) {
                this.getTaxInclCompileTotalSum = getTaxInclCompileTotalSum;
                return this;
            }

            public Builder<T> setTaxExclBaseTotalSum(BiConsumer<T, BigDecimal> setTaxExclBaseTotalSum) {
                this.setTaxExclBaseTotalSum = setTaxExclBaseTotalSum;
                return this;
            }

            public Builder<T> setTaxInclBaseTotalSum(BiConsumer<T, BigDecimal> setTaxInclBaseTotalSum) {
                this.setTaxInclBaseTotalSum = setTaxInclBaseTotalSum;
                return this;
            }

            public Builder<T> setTaxExclCompileTotalSum(BiConsumer<T, BigDecimal> setTaxExclCompileTotalSum) {
                this.setTaxExclCompileTotalSum = setTaxExclCompileTotalSum;
                return this;
            }

            public Builder<T> setTaxInclCompileTotalSum(BiConsumer<T, BigDecimal> setTaxInclCompileTotalSum) {
                this.setTaxInclCompileTotalSum = setTaxInclCompileTotalSum;
                return this;
            }

            public FieldAccessor<T> build() {
                return new FieldAccessor<>(this);
            }
        }
    }

    /**
     * 计算单位为%的工料机合价
     * 
     * 算法：
     * 1. 按 categoryId 汇总非%工料机的四个合价（分类总和）
     * 2. 遍历%工料机，用 calcBase.formula 和 calcBase.variables 计算公式结果
     * 3. 合价 = 公式结果 × (消耗量 / 100)
     *
     * @param topLevelResources 顶层工料机列表（用于构建分类总和，不含子工料机）
     * @param allResources 所有工料机列表（含子工料机，用于计算%合价）
     * @param accessor 字段访问器
     * @param <T> 工料机 VO 类型
     */
    @SuppressWarnings("unchecked")
    public static <T> void calculatePercentUnitTotalSums(
            List<T> topLevelResources,
            List<T> allResources,
            FieldAccessor<T> accessor) {
        
        if (topLevelResources == null || topLevelResources.isEmpty()) {
            return;
        }
        
        // 1. 构建类别价格映射表（跳过单位为%的工料机）
        // 复合工料机（有children）取父级合价，子工料机不单独累加
        Map<Long, BigDecimal[]> categoryPriceMap = new HashMap<>();
        
        for (T vo : topLevelResources) {
            // 跳过单位为 % 的工料机
            if ("%".equals(accessor.getUnit.apply(vo))) {
                continue;
            }
            
            Long categoryId = accessor.getCategoryId.apply(vo);
            if (categoryId != null) {
                BigDecimal[] sums = categoryPriceMap.computeIfAbsent(categoryId, 
                        k -> new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO});
                
                BigDecimal taxExclBase = accessor.getTaxExclBaseTotalSum.apply(vo);
                BigDecimal taxInclBase = accessor.getTaxInclBaseTotalSum.apply(vo);
                BigDecimal taxExclCompile = accessor.getTaxExclCompileTotalSum.apply(vo);
                BigDecimal taxInclCompile = accessor.getTaxInclCompileTotalSum.apply(vo);
                
                if (taxExclBase != null) sums[0] = sums[0].add(taxExclBase);
                if (taxInclBase != null) sums[1] = sums[1].add(taxInclBase);
                if (taxExclCompile != null) sums[2] = sums[2].add(taxExclCompile);
                if (taxInclCompile != null) sums[3] = sums[3].add(taxInclCompile);
            }
        }
        
        // 2. 处理单位为 % 的工料机
        for (T vo : allResources) {
            if (!"%".equals(accessor.getUnit.apply(vo))) {
                continue;
            }
            
            Map<String, Object> calcBase = accessor.getCalcBase.apply(vo);
            if (calcBase == null || !calcBase.containsKey("formula") || !calcBase.containsKey("variables")) {
                continue;
            }
            
            try {
                String formula = (String) calcBase.get("formula");
                Map<String, Object> variablesConfig = (Map<String, Object>) calcBase.get("variables");
                
                // 构建四组变量值映射表
                Map<String, BigDecimal> taxExclBaseVars = new HashMap<>();
                Map<String, BigDecimal> taxInclBaseVars = new HashMap<>();
                Map<String, BigDecimal> taxExclCompileVars = new HashMap<>();
                Map<String, BigDecimal> taxInclCompileVars = new HashMap<>();
                
                for (Map.Entry<String, Object> varEntry : variablesConfig.entrySet()) {
                    String varName = varEntry.getKey();
                    Long categoryId = Long.valueOf(varEntry.getValue().toString());
                    BigDecimal[] sums = categoryPriceMap.get(categoryId);
                    
                    if (sums == null) {
                        log.debug("[calculatePercentUnitTotalSums] 类别数据为空，使用0作为默认值，categoryId={}", categoryId);
                        taxExclBaseVars.put(varName, BigDecimal.ZERO);
                        taxInclBaseVars.put(varName, BigDecimal.ZERO);
                        taxExclCompileVars.put(varName, BigDecimal.ZERO);
                        taxInclCompileVars.put(varName, BigDecimal.ZERO);
                    } else {
                        taxExclBaseVars.put(varName, sums[0]);
                        taxInclBaseVars.put(varName, sums[1]);
                        taxExclCompileVars.put(varName, sums[2]);
                        taxInclCompileVars.put(varName, sums[3]);
                    }
                }
                
                // 计算公式结果
                BigDecimal taxExclBaseResult = FormulaEvaluator.evaluate(formula, taxExclBaseVars);
                BigDecimal taxInclBaseResult = FormulaEvaluator.evaluate(formula, taxInclBaseVars);
                BigDecimal taxExclCompileResult = FormulaEvaluator.evaluate(formula, taxExclCompileVars);
                BigDecimal taxInclCompileResult = FormulaEvaluator.evaluate(formula, taxInclCompileVars);
                
                // 合价 = 公式结果 × (消耗量 / 100)
                BigDecimal dosage = accessor.getEffectiveDosage.apply(vo);
                if (dosage != null) {
                    BigDecimal factor = dosage.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP);
                    accessor.setTaxExclBaseTotalSum.accept(vo, taxExclBaseResult.multiply(factor).setScale(4, RoundingMode.HALF_UP));
                    accessor.setTaxInclBaseTotalSum.accept(vo, taxInclBaseResult.multiply(factor).setScale(4, RoundingMode.HALF_UP));
                    accessor.setTaxExclCompileTotalSum.accept(vo, taxExclCompileResult.multiply(factor).setScale(4, RoundingMode.HALF_UP));
                    accessor.setTaxInclCompileTotalSum.accept(vo, taxInclCompileResult.multiply(factor).setScale(4, RoundingMode.HALF_UP));
                }
            } catch (Exception e) {
                log.error("[calculatePercentUnitTotalSums] 计算单位为%的工料机合价失败，error={}", e.getMessage(), e);
            }
        }
    }
    
    /**
     * 重新计算合价（价格 × 有效消耗量）
     * 跳过复合工料机（已汇总子项合价）和单位为%的工料机（由公式计算）
     *
     * @param resources 所有工料机列表
     * @param accessor 字段访问器
     * @param isMergedChecker 判断是否为复合工料机的函数
     * @param priceGetters 四个价格字段的 getter [taxExclBase, taxInclBase, taxExclCompile, taxInclCompile]
     * @param <T> 工料机 VO 类型
     */
    @SafeVarargs
    public static <T> void recalculateTotalSums(
            List<T> resources,
            FieldAccessor<T> accessor,
            Function<T, Boolean> isMergedChecker,
            Function<T, BigDecimal>... priceGetters) {
        
        if (resources == null || resources.isEmpty() || priceGetters.length != 4) {
            return;
        }
        
        for (T vo : resources) {
            BigDecimal effectiveDosage = accessor.getEffectiveDosage.apply(vo);
            if (effectiveDosage == null) {
                continue;
            }
            
            // 跳过复合工料机和单位为%的工料机
            Boolean isMerged = isMergedChecker.apply(vo);
            if (isMerged != null && isMerged) {
                continue;
            }
            if ("%".equals(accessor.getUnit.apply(vo))) {
                continue;
            }
            
            accessor.setTaxExclBaseTotalSum.accept(vo, multiplyOrZero(priceGetters[0].apply(vo), effectiveDosage));
            accessor.setTaxInclBaseTotalSum.accept(vo, multiplyOrZero(priceGetters[1].apply(vo), effectiveDosage));
            accessor.setTaxExclCompileTotalSum.accept(vo, multiplyOrZero(priceGetters[2].apply(vo), effectiveDosage));
            accessor.setTaxInclCompileTotalSum.accept(vo, multiplyOrZero(priceGetters[3].apply(vo), effectiveDosage));
        }
    }

    /**
     * 计算复合工料机父级的四个合价
     * 1. 先用兄弟级类别合价计算%子工料机的合价
     * 2. 再汇总所有子项合价到父级
     * 
     * 适用于父子类型相同的场景（如工作台 WbBoqResourceRespVO）
     *
     * @param parent 父级工料机
     * @param children 子工料机列表
     * @param accessor 字段访问器（父子共用）
     * @param <T> 工料机 VO 类型
     */
    public static <T> void calculateMergedParentTotalSums(
            T parent, List<T> children, FieldAccessor<T> accessor) {
        if (children == null || children.isEmpty()) {
            return;
        }
        // 1. 先计算%子工料机的合价（使用兄弟级类别合价）
        calculatePercentUnitTotalSums(children, children, accessor);
        // 2. 汇总所有子项合价到父级
        sumChildrenToParent(parent, children, accessor, accessor);
    }

    /**
     * 汇总子项合价到父级（支持父子不同类型）
     *
     * @param parent 父级工料机
     * @param children 子工料机列表
     * @param childAccessor 子级字段访问器（只用 getter）
     * @param parentAccessor 父级字段访问器（只用 setter）
     * @param <P> 父级类型
     * @param <C> 子级类型
     */
    public static <P, C> void sumChildrenToParent(
            P parent, List<C> children,
            FieldAccessor<C> childAccessor,
            FieldAccessor<P> parentAccessor) {
        if (children == null || children.isEmpty()) {
            return;
        }
        BigDecimal totalTaxExclBase = BigDecimal.ZERO;
        BigDecimal totalTaxInclBase = BigDecimal.ZERO;
        BigDecimal totalTaxExclCompile = BigDecimal.ZERO;
        BigDecimal totalTaxInclCompile = BigDecimal.ZERO;

        for (C child : children) {
            BigDecimal v0 = childAccessor.getTaxExclBaseTotalSum.apply(child);
            BigDecimal v1 = childAccessor.getTaxInclBaseTotalSum.apply(child);
            BigDecimal v2 = childAccessor.getTaxExclCompileTotalSum.apply(child);
            BigDecimal v3 = childAccessor.getTaxInclCompileTotalSum.apply(child);
            if (v0 != null) totalTaxExclBase = totalTaxExclBase.add(v0);
            if (v1 != null) totalTaxInclBase = totalTaxInclBase.add(v1);
            if (v2 != null) totalTaxExclCompile = totalTaxExclCompile.add(v2);
            if (v3 != null) totalTaxInclCompile = totalTaxInclCompile.add(v3);
        }

        parentAccessor.setTaxExclBaseTotalSum.accept(parent, totalTaxExclBase.setScale(4, RoundingMode.HALF_UP));
        parentAccessor.setTaxInclBaseTotalSum.accept(parent, totalTaxInclBase.setScale(4, RoundingMode.HALF_UP));
        parentAccessor.setTaxExclCompileTotalSum.accept(parent, totalTaxExclCompile.setScale(4, RoundingMode.HALF_UP));
        parentAccessor.setTaxInclCompileTotalSum.accept(parent, totalTaxInclCompile.setScale(4, RoundingMode.HALF_UP));
    }

    /**
     * 安全乘法：如果 value 为 null 返回 ZERO，否则返回 value * factor
     */
    public static BigDecimal multiplyOrZero(BigDecimal value, BigDecimal factor) {
        if (value == null) {
            return BigDecimal.ZERO;
        }
        return value.multiply(factor);
    }
}
