package com.brilliance.isc.common.fecism.calcfee;

import com.brilliance.mda.runtime.mda.impl.Argument;
import com.brilliance.mda.runtime.mda.util.*;
import com.brilliance.isc.bo.Fec;
import com.brilliance.isc.bo.Fee;
import com.brilliance.isc.common.cbsmod.CbsRange;
import com.brilliance.isc.common.cbsmod.service.XrtmodService;
import com.brilliance.isc.common.codetable.service.CodetableService;
import com.brilliance.isc.common.converter.DtoMapper;
import com.brilliance.isc.common.exception.CommonServiceException;
import com.brilliance.isc.common.fecism.service.FecismService;
import com.brilliance.isc.common.fecism.vo.CalcFeeRequest;
import com.brilliance.isc.common.fecism.vo.CalcFeeResponse;
import com.brilliance.isc.common.glemod.service.impl.GlemodServiceImpl;
import com.brilliance.isc.common.util.Utils;
import com.brilliance.isc.common.vo.FecVo;
import com.brilliance.isc.mda.dao.FeeMapper;
import com.brilliance.isc.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.util.Date;

import static com.brilliance.mda.runtime.mda.Constants.TAB;

public abstract class AbstractFeeCalculator implements IFeeCalculator{

    private static Logger log = LoggerFactory.getLogger(GlemodServiceImpl.class);

    public static final String CR = "\n";

    @Autowired
    private CodetableService codetableService;

    @Autowired
    private XrtmodService xrtmodService;

    @Autowired
    private FecismService fecismService;

    @Autowired
    private CbsRange cbsRange;

    @Autowired
    private FeeMapper feeMapper;

    @Override
    public CalcFeeResponse calcFee(CalcFeeRequest request) {
        printLog(request);
        CalcFeeResponse response = calculate(request);
        CalcFeeResponse defaultResponse = calculateByDefaultFec(request);
        if (defaultResponse != null) {
            updateCommCbsLineGrid(response, defaultResponse);
            response.setOgiamt(defaultResponse.getOgiamt());
        }
        //计算优惠比例
        if (response.getOgiamt().compareTo(BigDecimal.ZERO) != 0 && response.getAmt().compareTo(response.getOgiamt()) < 0) {
            BigDecimal dctamt = response.getOgiamt().subtract(response.getAmt());
            BigDecimal dctrat = dctamt.divide(response.getOgiamt(), 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100"));
            response.setDctrat(dctrat);
        }
        printLog(response);
        return response;
    }

    abstract protected CalcFeeResponse calculate(CalcFeeRequest request);

    public void updateCommCbsLineGrid(CalcFeeResponse response, CalcFeeResponse defaultResponse) {
        if (response.getCommCbsLineGrids() != null
                && defaultResponse.getCommCbsLineGrids() != null
                && response.getCommCbsLineGrids().size() == defaultResponse.getCommCbsLineGrids().size()) {
            for (int i=0; i<response.getCommCbsLineGrids().size(); i++) {
                response.getCommCbsLineGrids().get(i).setOgiamt(defaultResponse.getCommCbsLineGrids().get(i).getFeeamt());
            }
        }
    }
    /**
     * 使用默认fec计算应收金额
     * @param request
     * @return
     */
    public CalcFeeResponse calculateByDefaultFec(CalcFeeRequest request) {
        if (request.isReadByDefault()) {
            log.info("已经使用缺省计费规则计算过应收费用了");
            return null;
        }
        log.info("使用费用代码{}缺省计费规则计算应收费用", request.getFeecod());
        Fee fee = feeMapper.selectByCod(request.getFeecod());
        if (fee == null) {
            throw new CommonServiceException("找不到费用代码数据");
        }
        Fec fec = new Fec();
        fec.setFeepri("!");
        fec.setFeeinr(fee.getInr());
        Date date = Dates.add(Utils.getMaxDate(), -1);
        cbsRange.sdbRngReadDefault(fec, date);
        if (StringUtils.isNotEmpty(fec.getInr())) {
            FecVo fecVo = DtoMapper.INSTANCE.transfer(fec);
            DtoMapper.INSTANCE.updateFecToRequest(fecVo, request);
            request.setReadByDefault(true);
            return fecismService.clcFecAmt(request);
        }
        log.info("找不到缺省计费规则");
        return null;
    }

    public void buildInfdetstm(CalcFeeResponse response, String relcur, BigDecimal relamt) {
        // clear calculation Details
        MdaUtils.clear(response.getInfdetstm());
        // store relevant amount in DETSTM
        Streams.streamInsert(response.getInfdetstm(), 0, "RELAMT" + TAB + relcur + TAB + MdaUtils.toString(relamt));
    }

    public void buildInfdetstmDat2dft(CalcFeeResponse response, Date dat2dft) {
        // store 业务效期
        Streams.streamInsert(response.getInfdetstm(), 0, "DAT2DFT" + TAB + Utils.fmtDate(dat2dft));
    }

    public String buildInftxt(CalcFeeRequest request) {
        //String inftxt = Strings.getText("Condition from $1 used.", codetableService.getTblLabel(request.getFeepri(), "FEEPRI"));
        String feepriLabel = "";
        if ("!".equals(request.getFeepri())) {
            feepriLabel = "缺省";
        } else if ("4".equals(request.getFeepri())) {
            feepriLabel = "客户";
        } else if ("6".equals(request.getFeepri())) {
            feepriLabel = "业务";
        } else {
            feepriLabel = codetableService.getTblLabel(request.getFeepri(), "FEEPRI");
        }
        String inftxt = "";
        if (StringUtils.isNotEmpty(request.getInftxt())) {
            inftxt = request.getInftxt() + CR;
        }
        inftxt += Strings.getText("使用$1计费规则", feepriLabel);
        /*if (!"C".equals(request.getCalrul()) && !"P".equals(request.getCalrul())) {
            //inftxt += CR + Strings.getText("Calculation of $1 units of $3 ($2).", request.getUnt(), codetableService.getTblLabel(request.getCalrul(), "CALRUL"), codetableService.getTblLabel(request.getFeecod(), "FEETXT"));
            inftxt += CR + Strings.getText("本次收取$1单元$2", request.getUnt(), codetableService.getTblLabel(request.getFeecod(), "FEETXT"));
        }*/
        return inftxt;
    }

    /**
     * 比较最大最小
     * C -- 基于CBS的计算 不走此逻辑
     * P -- 每千/前一周期 不走此逻辑
     * 0 -- 固定值        不走此逻辑
     * @param response
     * @param request
     */
    public void compareMaxmin(CalcFeeResponse response, CalcFeeRequest request) {
        // To apply minium/maximum once per calculation, set $minmaxunits = 1 (this is standard now)
        // You may use $minmaxunits = line\\unt instead if minimum shall be applied for each unit, if necessary depending on line\\WRKFEC\\CALRUL
        // 2013.10 需求，有的费用需要乘以Unit
        int minmaxunits = 1;
        if ("X".equals(request.getAmtflg())) {
            minmaxunits = response.getUnt();
        }
        // Minimum calculation
        BigDecimal minamt = BigDecimal.ZERO;
        String mincur = request.getMincur();
        if (!MdaUtils.isEmpty(mincur)) {
            String exchangeInftxt = "";
            if (MdaUtils.compareTo(request.getMincur(), response.getCur()) != 0) {
                Argument<String> inftxtArg = new Argument<>("", "");
                minamt = xrtmodService.amtRndUnt(fecismService.fecIsmAmtConv(inftxtArg, mincur, Decimals.mul(request.getMinamt(), minmaxunits), response.getCur(), Dates.today()), response.getCur());
                if (StringUtils.isNotEmpty(inftxtArg.value)) {
                    exchangeInftxt = inftxtArg.value;
                    //response.setInftxt(response.getInftxt() + CR + inftxtArg.value);
                }
            } else {
                minamt = Decimals.mul(request.getMinamt(), minmaxunits);
            }
            /**
             if line\\amt < $minamt then
             line\\amt = $minamt
             **/
            //考虑优惠，上面代码作如下修改
            if (MdaUtils.compareTo(response.getOgiamt(), minamt) < 0) {
                response.setAmt(minamt);
                response.setOgiamt(minamt);
                response.setMinmaxflg("<");
                //response.setInftxt(response.getInftxt() + CR + Strings.getText("Minimum $3 $4  used.", response.getCur(), Utils.fmtAmount(response.getAmt(), response.getCur()), request.getMincur(), Utils.fmtAmount(request.getMinamt(), request.getMincur())));
                response.setInftxt(response.getInftxt() + CR + Strings.getText("使用最小金额$1 $2", request.getMincur(), Utils.fmtAmount(request.getMinamt(), request.getMincur())));
                if (StringUtils.isNotEmpty(exchangeInftxt)) {
                    response.setInftxt(response.getInftxt() + CR + exchangeInftxt);
                }
            }
        }
        //                                       Maximum calculation
        if (!MdaUtils.isEmpty(request.getMaxcur()) && !MdaUtils.isEmpty(request.getMaxamt())) {
            String exchangeInftxt = "";
            BigDecimal maxamt = new BigDecimal("0");
            if (MdaUtils.compareTo(request.getMaxcur(), response.getCur()) != 0) {
                Argument<String> inftxtArg = new Argument<>("", "");
                maxamt = xrtmodService.amtRndUnt(fecismService.fecIsmAmtConv(inftxtArg, request.getMaxcur(), Decimals.mul(request.getMaxamt(), minmaxunits), response.getCur(), Dates.today()), response.getCur());
                if (StringUtils.isNotEmpty(inftxtArg.value)) {
                    exchangeInftxt = inftxtArg.value;
                    //response.setInftxt(response.getInftxt() + CR + inftxtArg.value);
                }
            } else {
                maxamt = Decimals.mul(request.getMaxamt(), minmaxunits);
            }
            /**
             if line\\amt > $MAXAMT then
             line\\amt = $MAXAMT
             **/
            //考虑优惠，上面代码作如下修改
            if (MdaUtils.compareTo(response.getOgiamt(), maxamt) > 0) {
                response.setAmt(maxamt);
                response.setOgiamt(maxamt);
                response.setMinmaxflg(">");
                //response.setInftxt(response.getInftxt() + CR + Strings.getText("Maximum $3 $4 used.", response.getCur(), Utils.fmtAmount(response.getAmt(), response.getCur()), request.getMaxcur(), Utils.fmtAmount(request.getMaxamt(), request.getMaxcur())));
                response.setInftxt(response.getInftxt() + CR + Strings.getText("使用最大金额$1 $2", request.getMaxcur(), Utils.fmtAmount(request.getMaxamt(), request.getMaxcur())));
                if (StringUtils.isNotEmpty(exchangeInftxt)) {
                    response.setInftxt(response.getInftxt() + CR + exchangeInftxt);
                }
            }
        }
    }

    public BigDecimal getMinimum(BigDecimal val1, BigDecimal val2) {
        if (MdaUtils.compareTo(val1, val2) < 0) {
            return val1;
        } else {
            return val2;
        }
    }

    /**
     * 金额取整的处理逻辑
     * @param inftxt
     * @param feeamt
     * @param roundFlag
     * @return
     */
    public BigDecimal roundAmt(Argument<String> inftxt, BigDecimal feeamt, String roundFlag) {
        if ("Y".equals(roundFlag)) {
            //判断费用金额是否为整数
            if (feeamt.compareTo(feeamt.setScale(0, BigDecimal.ROUND_DOWN)) != 0) {
                inftxt.value = Strings.getText("金额取整$1", Numerics.round(feeamt, 0));
            }
            feeamt = Numerics.round(feeamt, 0);
        }
        return feeamt;
    }

    /**
     * 打印请求日志
     * @param request
     */
    public void printLog(CalcFeeRequest request) {
        log.info("开始使用新的计算方式计算费用，计算规则：{}", codetableService.getTblLabel(request.getCalrul(), "CALRUL"));
        log.info("请求入参{}", request.toString());
    }

    /**
     * 打印回复日志
     */
    public void printLog(CalcFeeResponse response) {
        log.info("计算费用逻辑结束,结果{}", response.toString());
    }
}
