package com.zt.plat.module.base.util;

import com.zt.plat.module.base.dal.dataobject.unitConversion.UnitConversionDO;
import com.zt.plat.module.base.dal.dataobject.quantityUnitRelation.QuantityUnitRelationDO;
import com.zt.plat.module.base.dal.dataobject.untInfo.UntInfoDO;
import lombok.extern.slf4j.Slf4j;

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

import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.zt.plat.module.unitmanagement.enums.UnitErrorCodeConstants.*;

/**
 * 单位转换工具类
 *
 * <p>支持三种转换策略：
 * <ul>
 *   <li>直接转换：使用预定义的转换规则</li>
 *   <li>基准单位转换：通过基准单位进行中转</li>
 *   <li>公式转换：使用自定义转换公式</li>
 * </ul>
 *
 * @author 系统
 */
@Slf4j
public class UnitConversionUtil {

    /**
     * 转换策略枚举
     */
    public enum ConversionStrategy {
        /** 直接转换 */
        DIRECT,
        /** 基准单位转换 */
        VIA_BASE_UNIT,
        /** 公式转换 */
        FORMULA,
        /** 无法转换 */
        NONE
    }

    /**
     * 转换结果
     */
    public static class ConversionResult {
        private final BigDecimal value;
        private final BigDecimal factor;
        private final ConversionStrategy strategy;
        private final String formula;

        public ConversionResult(BigDecimal value, BigDecimal factor, ConversionStrategy strategy, String formula) {
            this.value = value;
            this.factor = factor;
            this.strategy = strategy;
            this.formula = formula;
        }

        public BigDecimal getValue() {
            return value;
        }

        public BigDecimal getFactor() {
            return factor;
        }

        public ConversionStrategy getStrategy() {
            return strategy;
        }

        public String getFormula() {
            return formula;
        }
    }

    /**
     * 单位转换
     *
     * @param value 待转换的值
     * @param srcUntId 源单位ID
     * @param tgtUntId 目标单位ID
     * @param conversions 转换规则列表
     * @param relations 量纲-单位关联关系列表
     * @param precision 精度(小数位数)
     * @return 转换结果
     */
    public static ConversionResult convert(
            BigDecimal value,
            Long srcUntId,
            Long tgtUntId,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            int precision) {

        // 1. 如果源单位和目标单位相同，直接返回
        if (srcUntId.equals(tgtUntId)) {
            return new ConversionResult(value, BigDecimal.ONE, ConversionStrategy.DIRECT, "相同单位，无需转换");
        }

        // 2. 尝试直接转换
        ConversionResult directResult = tryDirectConversion(value, srcUntId, tgtUntId, conversions, precision);
        if (directResult != null) {
            return directResult;
        }

        // 3. 尝试通过基准单位转换
        ConversionResult viaBaseResult = tryViaBaseUnitConversion(value, srcUntId, tgtUntId, conversions, relations, precision);
        if (viaBaseResult != null) {
            return viaBaseResult;
        }

        // 4. 无法转换 - 抛出业务异常
        log.warn("无法找到从单位 {} 到单位 {} 的转换路径", srcUntId, tgtUntId);
        throw exception(UNIT_CONVERSION_PATH_NOT_FOUND,
            "ID:" + srcUntId, "ID:" + tgtUntId);
    }

    /**
     * 尝试直接转换（支持自动反向推导）
     */
    private static ConversionResult tryDirectConversion(
            BigDecimal value,
            Long srcUntId,
            Long tgtUntId,
            List<UnitConversionDO> conversions,
            int precision) {

        // 1. 查找正向转换规则：源 → 目标
        UnitConversionDO forwardConversion = conversions.stream()
                .filter(c -> c.getSrcUntId().equals(srcUntId) && c.getTgtUntId().equals(tgtUntId))
                .findFirst()
                .orElse(null);

        if (forwardConversion != null) {
            BigDecimal factor = forwardConversion.getFctr();
            BigDecimal result = value.multiply(factor).setScale(precision, RoundingMode.HALF_UP);
            String formula = forwardConversion.getFmu() != null ? forwardConversion.getFmu() :
                String.format("value * %s", factor);

            log.debug("直接转换(正向): {} (单位{}) * {} = {} (单位{})", value, srcUntId, factor, result, tgtUntId);
            return new ConversionResult(result, factor, ConversionStrategy.DIRECT, formula);
        }

        // 2. 查找反向转换规则：目标 → 源，然后取倒数
        UnitConversionDO reverseConversion = conversions.stream()
                .filter(c -> c.getSrcUntId().equals(tgtUntId) && c.getTgtUntId().equals(srcUntId))
                .findFirst()
                .orElse(null);

        if (reverseConversion != null) {
            // 反向系数：如果 目标→源 的系数是 f，则 源→目标 的系数是 1/f
            BigDecimal reverseFactor = reverseConversion.getFctr();
            BigDecimal factor = BigDecimal.ONE.divide(reverseFactor, Math.max(precision + 10, 20), RoundingMode.HALF_UP);
            BigDecimal result = value.multiply(factor).setScale(precision, RoundingMode.HALF_UP);
            String formula = String.format("value / %s (反向推导)", reverseFactor);

            log.debug("直接转换(反向推导): {} (单位{}) / {} = {} (单位{})", value, srcUntId, reverseFactor, result, tgtUntId);
            return new ConversionResult(result, factor, ConversionStrategy.DIRECT, formula);
        }

        return null;
    }

    /**
     * 尝试通过基准单位转换
     */
    private static ConversionResult tryViaBaseUnitConversion(
            BigDecimal value,
            Long srcUntId,
            Long tgtUntId,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            int precision) {

        // 1. 找到源单位和目标单位的量纲
        QuantityUnitRelationDO srcRelation = relations.stream()
                .filter(r -> r.getUntId().equals(srcUntId))
                .findFirst()
                .orElse(null);

        QuantityUnitRelationDO tgtRelation = relations.stream()
                .filter(r -> r.getUntId().equals(tgtUntId))
                .findFirst()
                .orElse(null);

        if (srcRelation == null || tgtRelation == null) {
            log.warn("找不到单位 {} 或 {} 的量纲关联关系", srcUntId, tgtUntId);
            return null;
        }

        // 2. 检查是否属于同一量纲
        if (!srcRelation.getUntQtyId().equals(tgtRelation.getUntQtyId())) {
            log.warn("单位 {} 和 {} 不属于同一量纲", srcUntId, tgtUntId);
            return null;
        }

        // 3. 找到基准单位
        Long quantityId = srcRelation.getUntQtyId();
        QuantityUnitRelationDO baseRelation = relations.stream()
                .filter(r -> r.getUntQtyId().equals(quantityId) && r.getIsBse() == 1)
                .findFirst()
                .orElse(null);

        if (baseRelation == null) {
            log.warn("量纲 {} 没有设置基准单位", quantityId);
            return null;
        }

        Long baseUnitId = baseRelation.getUntId();

        // 4. 源单位 → 基准单位
        BigDecimal toBaseValue;
        BigDecimal toBaseFactor;
        if (srcUntId.equals(baseUnitId)) {
            toBaseValue = value;
            toBaseFactor = BigDecimal.ONE;
        } else {
            ConversionResult toBaseResult = tryDirectConversion(value, srcUntId, baseUnitId, conversions, precision);
            if (toBaseResult == null) {
                log.warn("无法从源单位 {} 转换到基准单位 {}", srcUntId, baseUnitId);
                return null;
            }
            toBaseValue = toBaseResult.getValue();
            toBaseFactor = toBaseResult.getFactor();
        }

        // 5. 基准单位 → 目标单位
        BigDecimal finalValue;
        BigDecimal fromBaseFactor;
        if (tgtUntId.equals(baseUnitId)) {
            finalValue = toBaseValue;
            fromBaseFactor = BigDecimal.ONE;
        } else {
            ConversionResult fromBaseResult = tryDirectConversion(toBaseValue, baseUnitId, tgtUntId, conversions, precision);
            if (fromBaseResult == null) {
                log.warn("无法从基准单位 {} 转换到目标单位 {}", baseUnitId, tgtUntId);
                return null;
            }
            finalValue = fromBaseResult.getValue();
            fromBaseFactor = fromBaseResult.getFactor();
        }

        // 6. 计算总转换因子
        BigDecimal totalFactor = toBaseFactor.multiply(fromBaseFactor);
        String formula = String.format("通过基准单位(ID:%d): value * %s * %s", baseUnitId, toBaseFactor, fromBaseFactor);

        log.debug("基准单位转换: {} (单位{}) → {} (基准{}) → {} (单位{})",
                value, srcUntId, toBaseValue, baseUnitId, finalValue, tgtUntId);

        return new ConversionResult(finalValue, totalFactor, ConversionStrategy.VIA_BASE_UNIT, formula);
    }

    /**
     * 批量转换
     *
     * @param values 待转换的值列表
     * @param srcUntId 源单位ID
     * @param tgtUntId 目标单位ID
     * @param conversions 转换规则列表
     * @param relations 量纲-单位关联关系列表
     * @param precision 精度(小数位数)
     * @return 转换结果列表
     */
    public static List<ConversionResult> batchConvert(
            List<BigDecimal> values,
            Long srcUntId,
            Long tgtUntId,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            int precision) {

        return values.stream()
                .map(value -> convert(value, srcUntId, tgtUntId, conversions, relations, precision))
                .collect(Collectors.toList());
    }

    /**
     * 按单位符号转换（推荐使用）
     *
     * @param value 待转换的值
     * @param srcUnitSymbol 源单位符号（如: "kg", "m", "km"）
     * @param tgtUnitSymbol 目标单位符号
     * @param conversions 转换规则列表
     * @param relations 量纲-单位关联关系列表
     * @param units 单位信息列表
     * @param precision 精度(小数位数)
     * @return 转换结果
     */
    public static ConversionResult convertBySymbol(
            BigDecimal value,
            String srcUnitSymbol,
            String tgtUnitSymbol,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            List<UntInfoDO> units,
            int precision) {

        // 1. 根据符号查找单位
        UntInfoDO srcUnit = units.stream()
                .filter(u -> u.getSmb() != null && u.getSmb().equals(srcUnitSymbol))
                .findFirst()
                .orElseThrow(() -> exception(UNIT_NOT_FOUND, "符号:" + srcUnitSymbol));

        UntInfoDO tgtUnit = units.stream()
                .filter(u -> u.getSmb() != null && u.getSmb().equals(tgtUnitSymbol))
                .findFirst()
                .orElseThrow(() -> exception(UNIT_NOT_FOUND, "符号:" + tgtUnitSymbol));

        log.debug("按符号转换: {} ({}) → {} ({})", srcUnitSymbol, srcUnit.getId(), tgtUnitSymbol, tgtUnit.getId());

        // 2. 调用原有方法
        return convert(value, srcUnit.getId(), tgtUnit.getId(), conversions, relations, precision);
    }

    /**
     * 按单位名称转换
     *
     * @param value 待转换的值
     * @param srcUnitName 源单位名称（如: "千克", "米", "千米"）
     * @param tgtUnitName 目标单位名称
     * @param conversions 转换规则列表
     * @param relations 量纲-单位关联关系列表
     * @param units 单位信息列表
     * @param precision 精度(小数位数)
     * @return 转换结果
     */
    public static ConversionResult convertByName(
            BigDecimal value,
            String srcUnitName,
            String tgtUnitName,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            List<UntInfoDO> units,
            int precision) {

        // 1. 根据名称查找单位
        UntInfoDO srcUnit = units.stream()
                .filter(u -> u.getName() != null && u.getName().equals(srcUnitName))
                .findFirst()
                .orElseThrow(() -> exception(UNIT_NOT_FOUND, "名称:" + srcUnitName));

        UntInfoDO tgtUnit = units.stream()
                .filter(u -> u.getName() != null && u.getName().equals(tgtUnitName))
                .findFirst()
                .orElseThrow(() -> exception(UNIT_NOT_FOUND, "名称:" + tgtUnitName));

        log.debug("按名称转换: {} ({}) → {} ({})", srcUnitName, srcUnit.getId(), tgtUnitName, tgtUnit.getId());

        // 2. 调用原有方法
        return convert(value, srcUnit.getId(), tgtUnit.getId(), conversions, relations, precision);
    }

    /**
     * 按单位符号批量转换
     *
     * @param values 待转换的值列表
     * @param srcUnitSymbol 源单位符号
     * @param tgtUnitSymbol 目标单位符号
     * @param conversions 转换规则列表
     * @param relations 量纲-单位关联关系列表
     * @param units 单位信息列表
     * @param precision 精度(小数位数)
     * @return 转换结果列表
     */
    public static List<ConversionResult> batchConvertBySymbol(
            List<BigDecimal> values,
            String srcUnitSymbol,
            String tgtUnitSymbol,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            List<UntInfoDO> units,
            int precision) {

        return values.stream()
                .map(value -> convertBySymbol(value, srcUnitSymbol, tgtUnitSymbol, conversions, relations, units, precision))
                .collect(Collectors.toList());
    }

    /**
     * 按单位名称批量转换
     *
     * @param values 待转换的值列表
     * @param srcUnitName 源单位名称
     * @param tgtUnitName 目标单位名称
     * @param conversions 转换规则列表
     * @param relations 量纲-单位关联关系列表
     * @param units 单位信息列表
     * @param precision 精度(小数位数)
     * @return 转换结果列表
     */
    public static List<ConversionResult> batchConvertByName(
            List<BigDecimal> values,
            String srcUnitName,
            String tgtUnitName,
            List<UnitConversionDO> conversions,
            List<QuantityUnitRelationDO> relations,
            List<UntInfoDO> units,
            int precision) {

        return values.stream()
                .map(value -> convertByName(value, srcUnitName, tgtUnitName, conversions, relations, units, precision))
                .collect(Collectors.toList());
    }
}
