package com.ruoshui.flink.ao.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.ruoshui.flink.streaming.web.common.util.Md5Utils;
import com.ruoshui.flink.streaming.web.enums.AlarmLogStatusEnum;
import com.ruoshui.flink.streaming.web.enums.AlarmLogTypeEnum;
import com.ruoshui.flink.streaming.web.enums.DeployModeEnum;
import com.ruoshui.flink.streaming.web.model.dto.AlartLogDTO;
import com.ruoshui.flink.streaming.web.model.dto.JobConfigDTO;
import com.ruoshui.flink.streaming.web.model.dto.JobRunLogDTO;
import com.ruoshui.flink.ao.DingDingService;
import com.ruoshui.flink.service.AlartLogService;
import com.ruoshui.flink.service.JobRunLogService;
import com.ruoshui.flink.utils.HttpClientToolUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 
 * @author wxj
 * @date 2022年1月5日 下午3:02:00
 * @version V1.0
 */
@Component
@Slf4j
public class DingDingServiceImpl implements DingDingService {
    
    @Autowired
    private AlartLogService alartLogService;

    @Autowired
    private JobRunLogService jobRunLogService;

    @Override
    public void doAlarmNotify(String cusContent, JobConfigDTO jobConfigDTO, DeployModeEnum deployModeEnum) {
        Long id = jobConfigDTO.getId();
        if (StringUtils.isNotBlank(this.ALARM_DINGTALK_TELEPHONE)) {
            if (this.alarmDingTalkTasks.contains(id)) {
                String content = "大数据实时计算告警，" + cusContent;
                this.sendTelephoeNotify(id, this.ALARM_DINGTALK_TELEPHONE, this.DINGTALK_TEMPLATE_ID, 3, content);
            }
        }
        String un = this.ALARM_DINGMSG_USER;
        if (StringUtils.isNotBlank(un)) {
            if (this.alarmDingMsgTasks.contains(id)) {
                String content = "【大数据实时计算告警】\n" + cusContent + "\n通知时间：" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
                if (un.indexOf(",") > 0) {
                    for (String str : un.split(",")) {
                        if (StringUtils.isNotBlank(str)) {
                            this.sendDingDingMsg(id, str, content);
                        }
                    }
                } else {
                    this.sendDingDingMsg(id, this.ALARM_DINGMSG_USER, content);
                }
            }
        }
    }

    @NacosValue(value = "${DINGTALK_URL:}", autoRefreshed = true)
    private String DINGTALK_URL;

    @NacosValue(value = "${DINGTALK_TEMPLATE_ID:}", autoRefreshed = true)
    private String DINGTALK_TEMPLATE_ID;

    @NacosValue(value = "${DINGTALK_APPKEY:}", autoRefreshed = true)
    private String DINGTALK_APPKEY;

    @NacosValue(value = "${DINGMSG_NOTIFY_URL:}", autoRefreshed = true)
    private String DINGMSG_NOTIFY_URL;

    @NacosValue(value = "${DINGMSG_APP_ID:}", autoRefreshed = true)
    private String DINGMSG_APP_ID;

    @NacosValue(value = "${DINGMSG_APP_KEY:}", autoRefreshed = true)
    private String DINGMSG_APP_KEY;

    private Set<Long> alarmDingTalkTasks = new HashSet<Long>();

    private Set<Long> alarmDingMsgTasks = new HashSet<Long>();

    @NacosValue(value = "${ALARM_DINGTALK_TASKS:}", autoRefreshed = true)
    public void setAlarmDingTalkTasks(String alarmDingTalkTasks) {
        this.alarmDingTalkTasks.clear();
        if (StringUtils.isBlank(alarmDingTalkTasks)) {
            return;
        }
        String[] ids = alarmDingTalkTasks.split(",");
        for (String id : ids) {
            if (isNumeric(id)) {
                this.alarmDingTalkTasks.add(Long.valueOf(id));
            }
        }
    }

    @NacosValue(value = "${ALARM_DINGMSG_TASKS:}", autoRefreshed = true)
    public void setAlarmDingMsgTasks(String alarmDingMsgTasks) {
        this.alarmDingMsgTasks.clear();
        if (StringUtils.isBlank(alarmDingMsgTasks)) {
            return;
        }
        String[] ids = alarmDingMsgTasks.split(",");
        for (String id : ids) {
            if (isNumeric(id)) {
                this.alarmDingMsgTasks.add(Long.valueOf(id));
            }
        }
    }
    
    @NacosValue(value = "${ALARM_DINGTALK_TELEPHONE:}", autoRefreshed = true)
    private String ALARM_DINGTALK_TELEPHONE;
    
    @NacosValue(value = "${ALARM_DINGMSG_USER:}", autoRefreshed = true)
    private String ALARM_DINGMSG_USER;

    /**
     * 判断是否为数字
     * 
     * @param str
     * @return
     * @author wxj
     * @date 2022年1月5日 下午3:22:29 
     * @version V1.0
     */
    public static boolean isNumeric(String str) {
        for (int i = 0; i < str.length(); i++) {
            System.out.println(str.charAt(i));
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * 计算签名
     * 
     * @param param
     * @param appKey
     * @return
     * @author wxj
     * @date 2022年1月5日 下午3:22:08 
     * @version V1.0
     */
    public static String getSign(Map<String, String> param, String appKey) {
        Map<String, String> treeMap = new TreeMap<>(param);
        StringBuilder signBuilder = new StringBuilder();
        for (String key : treeMap.keySet()) {
            signBuilder.append("&").append(key).append("=").append(treeMap.get(key));
        }
        signBuilder.append(appKey);
        String signStr = StringUtils.substring(signBuilder.toString(), 1);  // 去掉第一个&
        return Md5Utils.MD5(signStr);
    }
    
    /**
     * 电话告警
     * 
     * @param paramMap
     * @return
     * @author wxj
     * @date 2022年1月5日 下午3:13:38 
     * @version V1.0
     */
    public JSONObject sendTelephoeNotify(Long jobConfigId, String mobile, String templateId, Integer count, String content) {
        Map<String, String> param = new HashMap<>();
        param.put("mobile", mobile);
        param.put("templateId", templateId);
        param.put("count", count.toString());
        param.put("param", content);
        String url = this.DINGTALK_URL;
        String sign = getSign(param, this.DINGTALK_APPKEY);
        param.put("sign", sign);
        JSONObject result = new JSONObject();
        try {
            String ret = HttpClientToolUtils.doPost(url, param, null, 6000);
            log.warn("电话告警，任务[{}]请求 {} 参数：mobile={},templateId={},count={},content={} \n返回：{}", 
                    jobConfigId, url, mobile, templateId, count, content, ret);
            if (StringUtils.isBlank(ret)) {
                result.put("ret", false);
                result.put("msg", "接口调用异常，返回为空");
                String failLog = "电话告警，任务["+jobConfigId+"]请求 "+url+" 参数：mobile="+mobile+",templateId="+templateId+",count="+count+",content="+content+" \n返回：" + result.toJSONString();
                this.insertLog(false, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
                return result;
            }
            JSONObject retJson = JSON.parseObject(ret);
            if (retJson.containsKey("code") && retJson.getInteger("code") != 200) {
                result.put("ret", false);
                result.put("msg", retJson.getString("msg"));
                String failLog = "电话告警，任务["+jobConfigId+"]请求 "+url+" 参数：mobile="+mobile+",templateId="+templateId+",count="+count+",content="+content+" \n返回：" + result.toJSONString();
                this.insertLog(false, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
                return result;
            }
            result.put("ret", true);
            result.put("msg", "成功");
            String failLog = "电话告警，任务["+jobConfigId+"]请求 "+url+" 参数：mobile="+mobile+",templateId="+templateId+",count="+count+",content="+content+" \n返回：" + result.toJSONString();
            this.insertLog(true, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
            return result;
        } catch (Exception e) {
            String failLog = "电话告警，任务["+jobConfigId+"]请求 "+url+" 参数：mobile="+mobile+",templateId="+templateId+",count="+count+",content="+content+" \n异常：" + e.getMessage();
            log.error(failLog, e);
            result.put("ret", false);
            result.put("msg", "接口调用失败");
            this.insertLog(false, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
            return result;
        }
    }
    
    /**
     * 钉钉消息告警
     * 
     * @param paramMap
     * @return
     * @author wxj
     * @date 2022年1月5日 下午3:13:38 
     * @version V1.0
     */
    public JSONObject sendDingDingMsg(Long jobConfigId, String un, String content) {
        Map<String, String> param = new HashMap<>();
        param.put("un", un);
        param.put("appid", this.DINGMSG_APP_ID);
        param.put("content", content);
        String url = this.DINGMSG_NOTIFY_URL;
        String sign = getSign(param, this.DINGMSG_APP_KEY);
        param.put("sign", sign);
        JSONObject result = new JSONObject();
        try {
            String ret = HttpClientToolUtils.doPost(url, param, null, 6000);
            log.warn("钉钉告警，任务[{}]请求 {} 参数：un={},content={} \n返回：{}", jobConfigId, url, un, content, ret);
            if (StringUtils.isBlank(ret)) {
                result.put("ret", false);
                result.put("msg", "接口调用异常，返回为空");
                String failLog = "钉钉告警，任务["+jobConfigId+"]请求 "+url+" 参数：un="+un+",content="+content+" \n返回：" + result.toJSONString();
                this.insertLog(false, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
                return result;
            }
            JSONObject retJson = JSON.parseObject(ret);
            if (retJson.containsKey("code") && retJson.getInteger("code") != 200) {
                result.put("ret", false);
                result.put("msg", retJson.getString("msg"));
                String failLog = "钉钉告警，任务["+jobConfigId+"]请求 "+url+" 参数：un="+un+",content="+content+" \n返回：" + result.toJSONString();
                this.insertLog(false, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
                return result;
            }
            result.put("ret", true);
            result.put("msg", "成功");
            String failLog = "钉钉告警，任务["+jobConfigId+"]请求 "+url+" 参数：un="+un+",content="+content+" \n返回：" + result.toJSONString();
            this.insertLog(true, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
            return result;
        } catch (Exception e) {
            String failLog = "钉钉告警，任务["+jobConfigId+"]请求 "+url+" 参数：un="+un+",content="+content+" \n异常：" + e.getMessage();
            log.error(failLog, e);
            result.put("ret", false);
            result.put("msg", "接口调用失败");
            this.insertLog(false, jobConfigId, failLog, content, AlarmLogTypeEnum.OTHER);
            return result;
        }
    }
    
    private void insertLog(boolean isSuccess, Long jobConfigId, String failLog, String content, AlarmLogTypeEnum alarMLogTypeEnum) {
        JobRunLogDTO jobRunLogDTO = jobRunLogService.getDetailLogById(jobConfigId);
        AlartLogDTO alartLogDTO = new AlartLogDTO();
        alartLogDTO.setJobConfigId(jobConfigId);
        alartLogDTO.setAlarMLogTypeEnum(alarMLogTypeEnum);
        alartLogDTO.setMessage(content);
        alartLogDTO.setFailLog(failLog);
        alartLogDTO.setJobName(jobRunLogDTO.getJobName());
        alartLogDTO.setAlarmLogStatusEnum(isSuccess ? AlarmLogStatusEnum.SUCCESS : AlarmLogStatusEnum.FAIL);
        alartLogService.addAlartLog(alartLogDTO);
    }
}
