package com.engine.salary.entity.salaryacct.bo;

import com.engine.salary.constant.SalaryFormulaFieldConstant;
import com.engine.salary.entity.salaryacct.po.SalaryAcctResultPO;
import com.engine.salary.entity.salaryformula.ExpressFormula;
import com.engine.salary.entity.salaryformula.po.FormulaVar;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.enums.salaryformula.SalaryFormulaReferenceEnum;
import com.engine.salary.util.SalaryEntityUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 薪资核算公式
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
public class ExpressFormulaBO {

    /**
     * 公式中变量的fieldId的正则表达式
     */
    private static final String SALARY_REGEX = "(\\w+)" + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR + "(\\w+)";

    /**
     * 解析公式中变量的fieldId的正则表达式
     */
    private static final Pattern SALARY_PATTERN = Pattern.compile(SALARY_REGEX);

    /**
     * 解析公式中的变量
     *
     * @param expressFormulas 公式详情
     * @return
     */
    public static Map<Long, List<FormulaVar>> buildFormulaVar(List<ExpressFormula> expressFormulas) {
        if (CollectionUtils.isEmpty(expressFormulas)) {
            return Collections.emptyMap();
        }
        Map<Long, List<FormulaVar>> resultMap = Maps.newHashMapWithExpectedSize(expressFormulas.size());
        for (ExpressFormula expressFormula : expressFormulas) {
            resultMap.put(expressFormula.getId(), expressFormula.getParameters());
        }
        return resultMap;
    }

    /**
     * 给公式中的变量填入值
     *
     * @param expressFormulas
     * @param salaryItems
     * @param acctResults
     * @return
     */
    public static Map<Long, List<FormulaVar>> buildFormulaVar4Check(List<ExpressFormula> expressFormulas, List<SalaryItemPO> salaryItems, List<SalaryAcctResultPO> acctResults) {
        if (CollectionUtils.isEmpty(expressFormulas)) {
            return Collections.emptyMap();
        }
        Map<Long, String> salaryItemCodeMap = SalaryEntityUtil.convert2Map(salaryItems, SalaryItemPO::getId, SalaryItemPO::getCode);
        Map<String, String> acctResultMap = SalaryEntityUtil.convert2Map(acctResults, e -> salaryItemCodeMap.getOrDefault(e.getSalaryItemId(), ""), SalaryAcctResultPO::getResultValue);
        Map<Long, List<FormulaVar>> formulaVarMap = buildFormulaVar(expressFormulas);
        formulaVarMap.forEach((k, v) -> {
            for (FormulaVar formulaVar : v) {
                Matcher matcher = SALARY_PATTERN.matcher(formulaVar.getFieldId());
                if (matcher.find()) {
                    SalaryFormulaReferenceEnum referenceEnum = SalaryFormulaReferenceEnum.parseByValue(matcher.group(1));
                    if (referenceEnum == SalaryFormulaReferenceEnum.SALARY_ITEM) {
                        formulaVar.setContent(acctResultMap.getOrDefault(matcher.group(2), ""));
                    }
                }
            }
        });
        return formulaVarMap;
    }

    /**
     * 给公式中的变量填入值
     *
     * @param expressFormula     公式
     * @param formulaVarValueMap 公式变量的值
     * @return
     */
    public static List<FormulaVar> buildFormulaVar4Accounting(ExpressFormula expressFormula, Map<String, String> formulaVarValueMap) throws JsonProcessingException {
        List<FormulaVar> formulaVars = new ArrayList<>();
        for (FormulaVar formulaVar : expressFormula.getParameters()) {
            // 公式变量的值
            String formulaVarValue = formulaVarValueMap.getOrDefault(formulaVar.getFieldId(), StringUtils.EMPTY);
            // 如果公式的返回值类型为number，公式中的变量的值如果为空，公式运行的时候会报错，所以需要替换成0
            if (StringUtils.isEmpty(formulaVarValue) && "number".equals(formulaVar.getFieldType())) {
                formulaVarValue = BigDecimal.ZERO.toPlainString();
            } else if (StringUtils.isEmpty(formulaVarValue) && "string".equals(formulaVar.getFieldType())) {
                formulaVarValue = "";
            }

            ObjectMapper objectMapper = new ObjectMapper();
            FormulaVar copyVar = objectMapper.readValue(objectMapper.writeValueAsString(formulaVar), FormulaVar.class);
            copyVar.setContent(formulaVarValue);
            formulaVars.add(copyVar);
        }
        return formulaVars;
    }
}
