package com.liquidnet.service.account.wallet.service;

import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.account.common.ErrorConstants;
import com.liquidnet.service.account.common.FinConstants;
import com.liquidnet.service.account.util.FinUtil;
import com.liquidnet.service.account.wallet.dto.base.WalletCallResult;
import com.liquidnet.service.account.wallet.dto.base.WalletContextParam;
import com.liquidnet.service.account.wallet.entity.FinBizTrade;
import com.liquidnet.service.account.wallet.entity.FinChannel;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDateTime;
import java.util.Map;

public abstract class WalletProcessorService {
    private static final Logger log = LoggerFactory.getLogger(WalletProcessorService.class);
    @Autowired
    private FinDictService dictService;
    @Autowired
    private FinChannelService channelService;
    @Autowired
    private FinBizTradeService bizTradeService;

    public WalletCallResult<?> service(WalletContextParam context) {
        WalletCallResult<?> paramsCheckResult = this.tradeParamsCheck(context);
        if (!paramsCheckResult.isSuccess()) {
            log.info("PubParameter check failed:[{}], context=[{}]", paramsCheckResult.getMessage(), JsonUtils.toJson(context));
            return paramsCheckResult;
        }
        WalletCallResult<?> paramCheckResult = checkInputParams(context);
        if (!paramCheckResult.isSuccess()) {
            log.info("PriParameter check failed:[{}], context=[{}]", paramCheckResult.getMessage(), JsonUtils.toJson(context));
            return paramCheckResult;
        }
        WalletCallResult<?> commonCheckResult = this.checkCommonLogic(context);
        if (!commonCheckResult.isSuccess()) {
            log.info("PubLogic check failed:[{}], context:[{}]", commonCheckResult.getMessage(), JsonUtils.toJson(context));
            return commonCheckResult;
        }
        WalletCallResult<?> bussinessCheckResult = checkBussinessLogic(context);
        if (!bussinessCheckResult.isSuccess()) {
            log.info("PriLogic check failed:[{}], context:[{}]", bussinessCheckResult.getMessage(), JsonUtils.toJson(context));
            return bussinessCheckResult;
        }
        context.setTradeNo(FinUtil.getTradeNo());
        context.setCreateTime(LocalDateTime.now());

        this.tradeRecordGeneration(context);
        log.info("Bussiness begin:[{}]", context.getTradeNo());
        WalletCallResult<?> result = doBussiness(context);
        log.info("Bussiness end:[{}]-{}", context.getTradeNo(), result.isSuccess());
        this.tradeRecordUpdation(context.getTradeNo(), result);
        return result;
    }

    /**
     * 公共参数校验
     *
     * @param context WalletContextParam
     * @return WalletCallResult<?>
     */
    protected WalletCallResult<?> tradeParamsCheck(WalletContextParam context) {
        if (StringUtils.isBlank(context.getChannelId())) {
            return new WalletCallResult<>(ErrorConstants.PARAMS_VALUE_ERROR_CODE, "Invalid channelId");
        }
        if (StringUtils.isBlank(context.getBizType())) {
            return new WalletCallResult<>(ErrorConstants.PARAMS_VALUE_ERROR_CODE, "Invalid bizType");
        }
        return new WalletCallResult<>();
    }

    protected WalletCallResult<?> checkCommonLogic(WalletContextParam context) {
        FinChannel channel = channelService.query(context.getChannelId());
        if (null == channel || !String.valueOf(FinConstants.Status.NORMAL.getVal()).equals(channel.getChannelStatus())) {
            return new WalletCallResult<>(ErrorConstants.PARAMS_VALUE_ERROR_CODE, (null == channel ? "Invalid" : "Disabled").concat(" channelId"));
        }
        Map<String, String> bizTypeDict = dictService.getDictValueForName(FinConstants.Dict.BIZ_TYPE.name());
        if (!bizTypeDict.containsValue(context.getBizType())) {
            return new WalletCallResult<>(ErrorConstants.PARAMS_VALUE_ERROR_CODE, "Unsupported bizType");
        }
        return new WalletCallResult<>();
    }

    /**
     * 输入参数必填项、值域等语法级别校验
     *
     * @param context WalletContextParam
     * @return WalletCallResult<?>
     */
    public abstract WalletCallResult<?> checkInputParams(WalletContextParam context);

    /**
     * 输入参数业务逻辑校验
     *
     * @param context WalletContextParam
     * @return WalletCallResult<?>
     */
    public abstract WalletCallResult<?> checkBussinessLogic(WalletContextParam context);


    public void tradeRecordGeneration(WalletContextParam context) {
        FinBizTrade bizTrade = FinBizTrade.getNew();
        BeanUtils.copyProperties(context, bizTrade);
        bizTrade.setTradeStatus(FinConstants.TxStatus.SUCCESS.getVal());
        bizTrade.setAdditional(JsonUtils.toJson(context));

        if (bizTradeService.addBizTrade(bizTrade) <= 0) {
            log.error("Add data failed[biz_trade]:[{}]", JsonUtils.toJson(bizTrade));
        }
    }

    /**
     * 真正业务逻辑
     *
     * @param context WalletContextParam
     * @return WalletCallResult<?>
     */
    public abstract WalletCallResult<?> doBussiness(WalletContextParam context);

    public void tradeRecordUpdation(String tradeNo, WalletCallResult<?> result) {
        try {
            if (result.isFailed()) {
                FinConstants.TxStatus rst = FinConstants.TxStatus.FAILED;

                if (bizTradeService.updateTradeStatus(tradeNo, rst.getVal()) <= 0) {
                    log.warn("Update data failed[biz_trade.status]:[{} -> {}]", tradeNo, rst.name());
                }

            }
        } catch (Exception e) {
            log.error("Update data abnormal[biz_trade]:{},[{}]", tradeNo, JsonUtils.toJson(result), e);
        }
    }
}
