package cn.com.ctop.alarm.modules.service.impl;

import cn.com.ctop.alarm.modules.entity.AlarmEventMetric;
import cn.com.ctop.alarm.modules.entity.AlarmEventRule;
import cn.com.ctop.alarm.modules.entity.AlarmEventSend;
import cn.com.ctop.alarm.modules.entity.AlarmMetricValue;
import cn.com.ctop.alarm.modules.entity.AlarmRuleCampaignRel;
import cn.com.ctop.alarm.modules.enums.StatusEnum;
import cn.com.ctop.alarm.modules.mapper.AlarmEventMetricMapper;
import cn.com.ctop.alarm.modules.mapper.AlarmEventRuleMapper;
import cn.com.ctop.alarm.modules.mapper.AlarmEventSendMapper;
import cn.com.ctop.alarm.modules.mapper.AlarmMetricValueMapper;
import cn.com.ctop.alarm.modules.mapper.AlarmRuleCampaignRelMapper;
import cn.com.ctop.alarm.modules.service.IAlarmEventSendService;
import cn.com.ctop.common.module.entity.CtopOauthToken;
import cn.com.ctop.common.module.service.ICtopOauthTokenService;
import cn.com.ctop.common.module.service.IMessageTemplate;
import cn.com.ctop.common.module.service.ISendMessageService;
import cn.com.ctop.common.module.utils.Check;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouCampaign;
import cn.com.ctop.kuaishou.modules.batch.entity.KuaiShouGroup;
import cn.com.ctop.kuaishou.modules.batch.mapper.KuaiShouCampaignMapper;
import cn.com.ctop.kuaishou.modules.batch.mapper.KuaiShouGroupMapper;
import cn.com.ctop.kuaishou.modules.batch.service.IKuaiShouUpdateService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxl.job.core.enums.NoEn;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.db.DataSourceCachePool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 预警事件发送表
 *
 * @author jeecg-boot
 * @version V1.0
 * @date 2020-10-29
 */
@Slf4j
@Service
public class AlarmEventSendServiceImpl extends ServiceImpl<AlarmEventSendMapper, AlarmEventSend> implements IAlarmEventSendService {

    @Autowired
    private AlarmEventRuleMapper alarmEventRuleMapper;

    @Autowired
    private AlarmEventMetricMapper alarmEventMetricMapper;

    @Autowired
    private AlarmRuleCampaignRelMapper alarmRuleCampaignRelMapper;

    @Autowired
    private AlarmMetricValueMapper alarmMetricValueMapper;

    @Autowired
    private AlarmEventSendMapper alarmEventSendMapper;

    @Autowired
    private IMessageTemplate messageTemplate;

    @Autowired
    private ISendMessageService sendMessageService;

    @Autowired
    private KuaiShouCampaignMapper kuaiShouCampaignMapper;

    @Autowired
    private IKuaiShouUpdateService kuaiShouUpdateService;

    @Autowired
    private ICtopOauthTokenService oauthTokenService;

    @Autowired
    private KuaiShouGroupMapper kuaiShouGroupMapper;

    private static JSONObject alarmValue;

    /**
     * value数据存储到内存
     *
     * @param
     * @return com.alibaba.fastjson.JSONObject
     * @throws
     * @author ZHAOXA
     */
    @Override
    public JSONObject getAlarmValue(String type) {
        if (NoEn.NO1.valueStr().equals(type)) {
            alarmValue = null;
        }
        if (Check.isNull(alarmValue)) {
            alarmValue = new JSONObject();
            List<AlarmMetricValue> valueList = alarmMetricValueMapper.selectList(null);
            if (!Check.isNull(valueList)) {
                for (AlarmMetricValue value : valueList) {
                    alarmValue.put(value.getMetricValueCode(), value);
                }
            }
            return alarmValue;
        }
        return alarmValue;
    }

    @Override
    public void matchAlarmRules(String checkType) {
        log.info("------匹配规则start----------");
        long startTime = System.currentTimeMillis();
        List<AlarmEventRule> allRules = getAllAlarmRules(checkType);
        List<JSONObject> accountCosts = getCostInfo("ACCOUNT");
        List<JSONObject> campaignCosts = getCostInfo("CAMPAIGN");
        List<JSONObject> unitCosts = getCostInfo("UNIT");
        if (Check.isNull(allRules)) {
            return;
        }
        //判断账户级
        for (AlarmEventRule rule : allRules) {
            for (JSONObject accountCost : accountCosts) {
                if ("ACCOUNT".equals(rule.getEventRuleLevel()) && accountCost.getLong("accountId") - rule.getAccountId() == 0) {
                    matchRules(rule, accountCost, "ACCOUNT");
                }
            }
            //判断计划级
            for (JSONObject campaignCost : campaignCosts) {
                if ("CAMPAIGN".equals(rule.getEventRuleLevel()) && campaignCost.getLong("accountId") - rule.getAccountId() == 0) {
                    List<AlarmRuleCampaignRel> rel = rule.getRel();
                    if (!Check.isNull(rel)) {
                        for (AlarmRuleCampaignRel campaignRel : rel) {
                            if (campaignRel.getCampaignId() - campaignCost.getLong("campaignId") == 0) {
                                rule.setCampaignId(campaignRel.getCampaignId());
                                rule.setCampaignName(campaignRel.getCampaignName());
                                matchRules(rule, campaignCost, "CAMPAIGN");
                            }
                        }
                    }
                }
            }
            //判断组级
            for (JSONObject unitCost : unitCosts) {
                if ("UNIT".equals(rule.getEventRuleLevel()) && unitCost.getLong("accountId") - rule.getAccountId() == 0) {
                    List<AlarmRuleCampaignRel> rel = rule.getRel();
                    if (!Check.isNull(rel)) {
                        for (AlarmRuleCampaignRel unitRel : rel) {
                            if (unitRel.getUnitId() - unitCost.getLong("unitId") == 0) {
                                rule.setCampaignId(unitRel.getCampaignId());
                                rule.setCampaignName(unitRel.getCampaignName());
                                rule.setUnitId(unitRel.getUnitId());
                                rule.setUnitName(unitRel.getUnitName());
                                matchRules(rule, unitCost, "UNIT");
                            }
                        }
                    }
                }
            }
        }
        long endTime = System.currentTimeMillis();
        log.info("---end---匹配规则用时：{} ms", endTime - startTime);
    }

    /**
     * 匹配规则
     *
     * @param
     * @return void
     * @throws
     * @author ZHAOXA
     */
    private void matchRules(AlarmEventRule rule, JSONObject cost, String level) {
        try {
            //目标值
            String goal = cost.getString(rule.getMetricValueCode());
            if (Check.isNull(goal)) {
                log.warn("规则中指标不匹配");
                return;
            }
            //总消耗
            BigDecimal totalCost = cost.getBigDecimal("totalCost");
            BigDecimal value = new BigDecimal(goal);
            List<AlarmEventMetric> metrics = rule.getAlarmEventMetrics();
            if (Check.isNull(metrics)) {
                return;
            }
            //value配置数据
            JSONObject alarmValue = getAlarmValue(NoEn.NO0.valueStr());
            boolean flag = true;
            StringBuffer _depletion = new StringBuffer();
            StringBuffer _roi = new StringBuffer();
            StringBuffer _value = new StringBuffer();
            for (AlarmEventMetric metric : metrics) {
                if (flag) {
                    //阈值
                    BigDecimal threshold = new BigDecimal(metric.getThreshold());
                    String judgeMethod = metric.getJudgeMethod();
                    String metricValue = metric.getMetricValueCode();
                    JSONObject valObj = alarmValue.getJSONObject(metricValue);
                    //模板
                    String msgTemplate = valObj.getString("msgTemplate");
                    //单位 元、%
                    String msgValueUnit = valObj.getString("msgValueUnit");
                    //阈值类型 1-字符串，2-数字（BigDecimal），3-百分数',
                    String showValue = getThreshold(valObj.getString("msgValueType"), threshold, msgValueUnit);
                    String judgment = "低于";
                    //大于等于
                    if ("GT_EQ".equals(judgeMethod)) {
                        judgment = "高于";
                        //判断阈值类型（cost-成本;depletion-消耗；roi)
                        if (metricValue.contains("_depletion")) {
                            flag = totalCost.compareTo(threshold) >= 0;
                            getMsgTemplate(_depletion, msgTemplate, judgment, "消耗值", showValue);
                        } else if (metricValue.contains("_roi")) {
                            flag = value.compareTo(threshold) >= 0;
                            getMsgTemplate(_roi, msgTemplate, judgment, "ROI", showValue);
                        } else {
                            flag = value.compareTo(threshold) >= 0;
                            getMsgTemplate(_value, msgTemplate, judgment, "阈值", showValue);
                        }
                        //小于等于
                    } else if ("LT_EQ".equals(judgeMethod)) {
                        if (metricValue.contains("_depletion")) {
                            flag = totalCost.compareTo(threshold) <= 0;
                            getMsgTemplate(_depletion, msgTemplate, judgment, "消耗值", showValue);
                        } else if (metricValue.contains("_roi")) {
                            flag = value.compareTo(threshold) <= 0;
                            getMsgTemplate(_roi, msgTemplate, judgment, "ROI", showValue);
                        } else {
                            flag = value.compareTo(threshold) <= 0;
                            getMsgTemplate(_value, msgTemplate, judgment, "阈值", showValue);
                        }
                        //介于
                    } else if ("BETWEEN".equals(judgeMethod)) {
                        BigDecimal minThreshold = new BigDecimal(metric.getMinThreshold());
                        String minshowValue = getThreshold(valObj.getString("msgValueType"), minThreshold, msgValueUnit);
                        if (metricValue.contains("_depletion")) {
                            flag = (totalCost.compareTo(threshold) > 0) || (totalCost.compareTo(minThreshold) < 0);
                            if (totalCost.compareTo(threshold) > 0) {
                                judgment = "高于";
                            }
                            getMsgTemplate(_depletion, msgTemplate, judgment, "消耗区间", minshowValue.concat("-").concat(showValue));
                        } else if (metricValue.contains("_roi")) {
                            flag = (value.compareTo(threshold) > 0) || (value.compareTo(minThreshold) < 0);
                            if (value.compareTo(threshold) > 0) {
                                judgment = "高于";
                            }
                            getMsgTemplate(_roi, msgTemplate, judgment, "ROI区间", minshowValue.concat("-").concat(showValue));
                            continue;
                        } else {
                            flag = (value.compareTo(threshold) > 0) || (value.compareTo(minThreshold) < 0);
                            if (value.compareTo(threshold) > 0) {
                                judgment = "高于";
                            }
                            getMsgTemplate(_value, msgTemplate, judgment, "阈值区间", minshowValue.concat("-").concat(showValue));
                        }
                        //等于
                    } else if ("EQ".equals(judgeMethod)) {
                        judgment = "等于";
                        flag = value.compareTo(threshold) == 0;
                        String name = StatusEnum.getEnumByCode(threshold.toString()).getName();
                        getMsgTemplate(_value, msgTemplate, judgment, "阈值", name);
                    } else {
                        log.warn("缺失判断条件");
                        break;
                    }
                } else {
                    break;
                }
            }
            //规则满足，产生预警
            if (flag) {
                String detail = getDetail(_depletion, _roi, _value);
                String msg = getMsg(level, detail, cost, rule);
                if (!Check.isNull(msg)) {
                    sendMessageService.sendMessage(rule.getUserId(), msg);
                    AlarmEventSend send = new AlarmEventSend();
                    send.setAlarmDetail(msg);

                    send.setRuleDimension(rule.getMetricValueCode());send.setAlarmStatus(NoEn.NO1.valueStr());
                    send.setGroupId(rule.getEventRuleId());
                    send.setGroupName(rule.getMetricValueName().concat("预警：").concat(detail.replace("<br/>", "；")));
                    send.setUserId(rule.getUserId());
                    alarmEventSendMapper.insert(send);
                }
            }
        } catch (Exception e) {
            log.error("匹配规则异常", e);
        }
    }

    private boolean shutDown(AlarmEventRule rule, String level) {
        CtopOauthToken token = oauthTokenService.getTokenByAccountId(rule.getAccountId());
        Map<String, Object> updateMap = new HashMap<>();
        if (!Check.isNull(token)) {
            if ("CAMPAIGN".equals(level)) {
                updateMap = kuaiShouUpdateService.updateCampaignStatus(token.getAccessToken(), rule.getAccountId(), rule.getCampaignId(), NoEn.NO2.valueInt(), rule.getUserId());
                log.info("---------规则关停快手计划：{}", rule.getCampaignId());
            } else if ("UNIT".equals(level)) {
                updateMap = kuaiShouUpdateService.updateUnitStatus(token.getAccessToken(), rule.getAccountId(), rule.getUnitId(), NoEn.NO2.valueInt(), rule.getUserId());
                log.info("---------规则关停快手组：{}", rule.getUnitId());
            }
        }
        return (boolean) updateMap.get("success");
    }

    /**
     * 判断模板中阈值展示格式（含单位）
     * 例如：100.0元、55.53%
     */
    private String getThreshold(String msgValueType, BigDecimal value, String msgValueUnit) {
        //1-字符串，2-数字（BigDecimal），3-百分数
        if ("3".equals(msgValueType)) {
            return value.multiply(new BigDecimal(100)).setScale(2).toString().concat(msgValueUnit);
        }
        return value.toString().concat(msgValueUnit);
    }

    /**
     * 匹配消息模板
     */
    private void getMsgTemplate(StringBuffer buf, String msgTemplate, String jud, String name, String value) {
        buf.append(msgTemplate.replace("{judgment}", jud).replace("{name}", name).replace("{value}", value));
    }

    /**
     * 组合消息内容
     */
    private String getDetail(StringBuffer _depletion, StringBuffer _roi, StringBuffer _value) {
        StringBuffer detail = new StringBuffer();
        if (_depletion.length() > 0) {
            detail.append(_depletion);
            if (_roi.length() > 0) {
                detail.append("并且").append(_roi);
                if (_value.length() > 0) {
                    detail.append("并且").append(_value);
                }
            } else {
                if (_value.length() > 0) {
                    detail.append("并且").append(_value);
                }
            }
        } else {
            if (_roi.length() > 0) {
                detail.append(_roi);
                if (_value.length() > 0) {
                    detail.append("并且").append(_value);
                }
            } else {
                if (_value.length() > 0) {
                    detail.append(_value);
                }
            }
        }
        return detail.toString();
    }

    /**
     * 整理发送的消息
     */
    private String getMsg(String level, String detail, JSONObject cost, AlarmEventRule rule) {
        StringBuffer header = new StringBuffer();
        header.append(rule.getMetricValueName()).append("预警").append("<br/>").
                append("您的账户：").append(cost.getString("accountId")).append("，授权名称为：").append(rule.getAccountName()).append("<br/>");
        if ("ACCOUNT".equals(level)) {
            header.append(detail);
        } else if ("CAMPAIGN".equals(level)) {
            header.append("计划ID：").append(rule.getCampaignId()).append("，计划名称为：").append(rule.getCampaignName()).append("<br/>").append(detail);
        } else {
            header.append("计划ID：").append(rule.getCampaignId()).append("，计划名称为：").append(rule.getCampaignName()).append("<br/>")
                    .append("组ID：").append(rule.getUnitId()).append("，组名称为：").append(rule.getUnitName()).append("<br/>").append(detail);
        }
        String msg = null;
        if ("PAUSE".equals(rule.getProcessMethod())) {
            header.append("现已被关停，请您及时查看并调整！");
            if (shutDown(rule, level)) {
                msg = header.toString();
            }
        } else {
            header.append("请您及时查看并调整！");
            msg = header.toString();
        }
        return msg;
    }


    /**
     * 获取成本数据。
     *
     * @param code : unit 组级数据，account 账户级数据
     * @return java.util.List<cn.com.ctop.kuaishou.modules.report.entity.KuaishouCostGroup>
     * @throws
     * @author ZHAOXA
     */
    private List<JSONObject> getCostInfo(String code) {
        HashOperations<String, Object, Object> hahs = DataSourceCachePool.getRedisTemplate().opsForHash();
        return (List<JSONObject>) hahs.get("KS_COST", code);
    }

    /**
     * 获取告警规则集合
     *
     * @param
     * @return java.util.List<cn.com.ctop.alarm.modules.entity.AlarmEventRules>
     * @throws
     * @author ZHAOXA
     */
    private List<AlarmEventRule> getAllAlarmRules(String checkType) {
        AlarmEventRule alarmEventRule = new AlarmEventRule();
        alarmEventRule.setEventRuleStatus(NoEn.NO1.valueStr());
        alarmEventRule.setCheckFrequency(checkType);
        List<AlarmEventRule> ruleList = alarmEventRuleMapper.selectListPage(alarmEventRule);
        if (!Check.isNull(ruleList)) {
            for (AlarmEventRule rule : ruleList) {
                rule.setAlarmEventMetrics(alarmEventMetricMapper.selectByEventRuleId(rule.getEventRuleId()));
                if (!"ACCOUNT".equals(rule.getEventRuleLevel())) {
                    if (NoEn.NO1.valueStr().equals(rule.getAppStatus())) {
                        rule.setRel(getRel(rule));
                    } else {
                        rule.setRel(alarmRuleCampaignRelMapper.selectByRuleId(rule.getEventRuleId()));
                    }
                }
            }
        }
        return ruleList;
    }

    private List<AlarmRuleCampaignRel> getRel(AlarmEventRule rule) {
        List<AlarmRuleCampaignRel> relList = new ArrayList<>();
        AlarmRuleCampaignRel rel = null;
        if ("CAMPAIGN".equals(rule.getEventRuleLevel())) {
            Map<String, Object> map = new HashMap<>();
            map.put("put_status", NoEn.NO1.valueStr());
            map.put("account_id", rule.getAccountId());
            List<KuaiShouCampaign> kuaiShouCampaigns = kuaiShouCampaignMapper.selectByMap(map);
            if (!Check.isNull(kuaiShouCampaigns)) {
                for (KuaiShouCampaign campaign : kuaiShouCampaigns) {
                    rel = new AlarmRuleCampaignRel();
                    rel.setCampaignId(campaign.getCampaignId());
                    rel.setCampaignName(campaign.getCampaignName());
                    rel.setEventRuleLevel("CAMPAIGN");
                    relList.add(rel);
                }
            }
        }
        if ("UNIT".equals(rule.getEventRuleLevel())) {
            Map<String, Object> map = new HashMap<>();
            map.put("putStatus", NoEn.NO1.valueStr());
            map.put("accountId", rule.getAccountId());
            if (Check.isNull(rule.getCampaignId())) {
                map.put("campaignId", rule.getCampaignId());
            }
            List<KuaiShouGroup> kuaiShouGroups = kuaiShouGroupMapper.queryByMap(map);
            if (!Check.isNull(kuaiShouGroups)) {
                for (KuaiShouGroup kuaiShouGroup : kuaiShouGroups) {
                    rel = new AlarmRuleCampaignRel();
                    rel.setCampaignId(kuaiShouGroup.getCampaignId());
                    rel.setCampaignName(kuaiShouGroup.getCampaignName());
                    rel.setEventRuleLevel("CAMPAIGN");
                    rel.setUnitId(kuaiShouGroup.getUnitId());
                    rel.setUnitName(kuaiShouGroup.getUnitName());
                    relList.add(rel);
                }
            }
        }
        return relList;
    }
}
