package com.quantgroup.asset.distribution.service.newrule;

import cn.quantgroup.motan.bean.UserAssociationBean;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.quantgroup.asset.distribution.constant.CommonConstants;
import com.quantgroup.asset.distribution.exception.QGException;
import com.quantgroup.asset.distribution.exception.QGExceptionType;
import com.quantgroup.asset.distribution.service.alarm.IAlarmService;
import com.quantgroup.asset.distribution.service.alarm.impl.MarkdownMessage;
import com.quantgroup.asset.distribution.service.httpclient.IHttpService;
import com.quantgroup.asset.distribution.service.jpa.entity.*;
import com.quantgroup.asset.distribution.service.jpa.repository.IChannelRuleRepository;
import com.quantgroup.asset.distribution.service.jpa.repository.IFundProductRepository;
import com.quantgroup.asset.distribution.service.jpa.repository.IRoutingRecordRepository;
import com.quantgroup.asset.distribution.service.jpa.repository.IWhiteListRepository;
import com.quantgroup.asset.distribution.service.newrule.pojo.RoutingRecordVO;
import com.quantgroup.asset.distribution.service.newrule.pojo.RuleConstant;
import com.quantgroup.asset.distribution.service.newrule.pojo.enums.AreaEnum;
import com.quantgroup.asset.distribution.service.newrule.pojo.enums.CooperationEnum;
import com.quantgroup.asset.distribution.service.newrule.pojo.enums.IdCardExpireEnum;
import com.quantgroup.asset.distribution.service.newrule.pojo.funds.*;
import com.quantgroup.asset.distribution.service.newrule.service.IProductRuleService;
import com.quantgroup.asset.distribution.service.newrule.third.AuditResponce;
import com.quantgroup.asset.distribution.service.user.IUserCenterService;
import com.quantgroup.asset.distribution.util.IdCardUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import shaded.com.google.common.collect.Lists;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author shihuajun
 * @date 2021/8/27 14:12
 * @ describing
 */
@Slf4j
@Service
public class CoreFilter {

    private static final String AUDIT_FAIL_TEXT = "通知业务系统审核结果失败，uuid 是 %s";

    @Resource
    private IHttpService httpService;
    @Resource
    private IAlarmService alarmService;
    @Resource
    private RuleHandleFactory ruleHandleFactory;
    @Resource
    private IProductRuleService productRuleService;
    @Resource
    private IFundProductRepository fundProductRepository;
    @Resource
    private IWhiteListRepository whiteListRepository;
    @Resource
    private IUserCenterService userCenterService;
    @Resource
    private IChannelRuleRepository channelRuleRepository;
    @Resource
    private IRoutingRecordRepository routingRecordRepository;

    @Transactional(rollbackFor = Exception.class)
    public void coreHandle(AuditResponce auditResponce){
        List<ChannelRuleEntity> channelRuleEntityList = channelRuleRepository.getByChannelId(Long.parseLong(auditResponce.getBizChannel()));
        if (CollectionUtils.isEmpty(channelRuleEntityList)){
            throw new RuntimeException("渠道配置产品集不存在，运营人员配置有问题");
        }
        UserAssociationBean associationBean = userCenterService.getUserAssociationBean(auditResponce.getUuid());
        List<FinanceProduct> financeProducts = new ArrayList<>();
        List<RoutingRecordVO> routingRecordList = new ArrayList<>();
        for (ChannelRuleEntity channelRuleEntity : channelRuleEntityList) {
            FundProductEntity fundProduct = fundProductRepository.getByIdEquals(channelRuleEntity.getFundProductId());
            if (fundProduct == null){
                log.error("这种情况不可能出现，需要看下原因 {}",JSON.toJSONString(channelRuleEntity));
                continue;
            }
            if (currentVerify(fundProduct,auditResponce,associationBean,channelRuleEntity)){
                financeProducts.add(build(auditResponce,fundProduct,channelRuleEntity));
            }
            routingRecordList.add(generateRouteRecord(channelRuleEntity));
        }
        FilterResult result = new FilterResult();
        result.setFinanceProducts(JSON.toJSONString(financeProducts));
        Map<String,String> response = httpService.postHasResponse(auditResponce.getCallbackUrl(), result.get(auditResponce));
        if(MapUtils.isEmpty(response) || !(HttpStatus.OK.value() + "").equals(response.get("statusCode"))
                || (!"success".equals(response.get("response")) && (!JSONObject.isValidObject(response.get("response"))
                || 0 != JSONObject.parseObject(response.get("response")).getInteger("code").intValue()))) {
            MarkdownMessage markdownMessage = new MarkdownMessage();
            markdownMessage.setTitle("通知业务系统审核结果失败告警");
            markdownMessage.add(String.format(AUDIT_FAIL_TEXT,auditResponce.getUuid()));
            alarmService.sendMsgWithAlert(markdownMessage);
            log.error("通知业务系统审核结果失败 {}",JSON.toJSONString(auditResponce));
            throw new QGException(QGExceptionType.NOTIFY_BIZ_ERROR);
        } else {
            RoutingRecordEntity routingRecordEntity = new RoutingRecordEntity();
            routingRecordEntity.setCreditNo(auditResponce.getBizNo());
            routingRecordEntity.setPhone(associationBean.getPhoneNo());
            routingRecordEntity.setUuid(auditResponce.getUuid());
            if (CollectionUtils.isEmpty(financeProducts)){
                routingRecordEntity.setStatus(new Byte("0"));
            }else {
                routingRecordEntity.setStatus(new Byte("1"));
                routingRecordEntity.setRoutedResult(JSON.toJSONString(routingRecordList));
            }
            routingRecordRepository.save(routingRecordEntity);
        }
    }

    private RoutingRecordVO generateRouteRecord(ChannelRuleEntity channelRuleEntity){
        RoutingRecordVO routingRecordVO = new RoutingRecordVO();
        BeanUtils.copyProperties(channelRuleEntity,routingRecordVO);
        return routingRecordVO;
    }

    private FinanceProduct build(AuditResponce auditResponce, FundProductEntity fundProduct,ChannelRuleEntity channelRuleEntity){
        FundInfo fundInfo = new FundInfo();
        fundInfo.setFundId(fundProduct.getFundId());
        fundInfo.setFundProductId(fundProduct.getFundProId() + "");
        fundInfo.setCooperation(CooperationEnum.parse(fundProduct.getOrgType()).ordinal());
        Terms term = new Terms();
        term.setTerm(Integer.parseInt(auditResponce.getTerm()));
        term.setFundInfos(Lists.newArrayList(fundInfo));
        FinanceProduct financeProduct = new FinanceProduct();
        financeProduct.setTerms(Lists.newArrayList(term));
        financeProduct.setMax(new BigDecimal(auditResponce.getAmount()));
        financeProduct.setMin(new BigDecimal(auditResponce.getAmount()));
        financeProduct.setWaitFlg(channelRuleEntity.getRouteWait().equals(new Byte("1")));
        return financeProduct;
    }

    private Boolean currentVerify(FundProductEntity fundProduct,AuditResponce auditResponce,
                                  UserAssociationBean associationBean,ChannelRuleEntity channelRuleEntity){
        if (fundProduct.getEnable().equals(new Byte("0"))){
            return Boolean.FALSE;
        }
        WhiteListEntity whiteListEntity = whiteListRepository.getByPhoneEquals(associationBean.getPhoneNo());
        Boolean basicInfoVerify = basicInfoVerify(fundProduct,associationBean,auditResponce,channelRuleEntity);
        if (whiteListEntity != null && basicInfoVerify){
            return Boolean.TRUE;
        }
        return specialInfoVerify(fundProduct, associationBean) && basicInfoVerify;
    }

    private Boolean basicInfoVerify(FundProductEntity fundProduct, UserAssociationBean associationBean,
                                    AuditResponce auditResponce, ChannelRuleEntity channelRuleEntity){
        if (StringUtils.isBlank(fundProduct.getBasicRule())){
            log.error("后台基础配置出问题了，资方产品信息是 {}", JSON.toJSONString(fundProduct));
            throw new RuntimeException("后台基础配置出问题了");
        }
        long channelId = channelRuleEntity.getChannelId();
        if (channelId != Long.parseLong(auditResponce.getBizChannel())){
            return Boolean.FALSE;
        }
        if (StringUtils.isNotBlank(auditResponce.getCreditLevel()) || (StringUtils.isNotBlank(auditResponce.getCreditLevel())
                && !channelRuleEntity.getUserLevel().contains(auditResponce.getCreditLevel()))) {
            return Boolean.FALSE;
        }
        if (StringUtils.isNotBlank(auditResponce.getUserTag()) || (StringUtils.isNotBlank(auditResponce.getUserTag())
                && !channelRuleEntity.getUserTag().contains(auditResponce.getUserTag()))){
            return Boolean.FALSE;
        }
        String basicRule = fundProduct.getBasicRule();
        Expression compiledExp = AviatorEvaluator.compile(basicRule);
        List<ProductRuleEntity> ruleEntityList = productRuleService.getAll();
        Map<String,Object> env = Maps.newConcurrentMap();
        if (StringUtils.isNotBlank(fundProduct.getTelRule())){
            env.put(RuleConstant.NOT_PERMIT_TEL, Lists.newArrayList(Arrays.stream(fundProduct.getTelRule()
                    .split(CommonConstants.COMMA)).mapToInt(s -> Integer.parseInt(s))));
        }
        for (ProductRuleEntity pre : ruleEntityList) {
            if (pre.getEnable().equals(new Byte("0"))){
                continue;
            }
            String currentRuleVal = pre.getRuleVal();
            if (!basicRule.contains(currentRuleVal)){
                continue;
            }
            if (currentRuleVal.equals(RuleConstant.AMOUNT)){
                env.put(currentRuleVal, new BigDecimal(auditResponce.getAmount()));
            }
            if (currentRuleVal.equals(RuleConstant.TERM)){
                env.put(currentRuleVal, Integer.parseInt(auditResponce.getTerm()));
            }
            int age = IdCardUtil.getAge(associationBean.getIdNo());
            env.put(RuleConstant.AGE, age);
            if (currentRuleVal.equals(RuleConstant.NOT_PERMIT_TEL)){
                env.put(RuleConstant.NOT_PERMIT_TELS,Integer.parseInt(StringUtils.substring(associationBean.getPhoneNo(),0,3)));
            }
        }
        Boolean execute = (Boolean) compiledExp.execute(env);
        log.info("基础校验结果 {}",execute);
        if (execute){
            execute = commonVerify(fundProduct,associationBean,auditResponce);
        }
        log.info("公共校验结果 {}",execute);
        return execute;
    }

    private Boolean commonVerify(FundProductEntity fundProduct,UserAssociationBean associationBean,AuditResponce auditResponce){
        for (IRuleVerifyService ruleService : RuleHandleFactory.COMMON_VERIFY) {
            RuleParam ruleParam = new RuleParam();
            ruleParam.setUserInfo(associationBean);
            ruleParam.setAreaNum(AreaEnum.parse(fundProduct.getAreaTerm()));
            ruleParam.setArea(fundProduct.getAreaRule());
            ruleParam.setUuid(auditResponce.getUuid());
            ruleParam.setCardExpireEnum(IdCardExpireEnum.parse(fundProduct.getCardLimit()));
            if (!ruleService.pass(ruleParam)){
                log.info("当前用户公共校验不通过 {}", JSON.toJSONString(associationBean));
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }

    private Boolean specialInfoVerify(FundProductEntity fundProduct,UserAssociationBean associationBean){
        String ruleList = fundProduct.getRuleList();
        if (StringUtils.isBlank(ruleList)){
            return Boolean.TRUE;
        }
        String[] rules = ruleList.split(CommonConstants.COMMA);
        for (String id : rules) {
            ProductRuleEntity productRuleEntity = productRuleService.getProductRuleEntityById(Long.parseLong(id));
            IRuleVerifyService ruleVerifyService = ruleHandleFactory.getRuleVerifyServiceMap(productRuleEntity.getRuleEngine());
            RuleParam ruleParam = new RuleParam();
            ruleParam.setUserInfo(associationBean);
            if (!ruleVerifyService.pass(ruleParam)){
                log.info("当前用户其他规则检验不通过 {}", JSON.toJSONString(associationBean));
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }
}
