package com.qmth.distributed.print.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.distributed.print.business.entity.BasicMessage;
import com.qmth.distributed.print.business.entity.ExamTask;
import com.qmth.distributed.print.business.enums.MessageEnum;
import com.qmth.distributed.print.business.mapper.BasicMessageMapper;
import com.qmth.distributed.print.business.service.BasicMessageService;
import com.qmth.distributed.print.business.service.ExamTaskService;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SpringContextHolder;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysConfig;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.service.CommonCacheService;
import com.qmth.teachcloud.common.service.SysConfigService;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 短信消息发送
 * @Author: CaoZixuan
 * @Date: 2021-04-28
 */
@Service
public class BasicMessageServiceImpl extends ServiceImpl<BasicMessageMapper, BasicMessage> implements BasicMessageService {
    @Resource
    private SysConfigService sysConfigService;
    @Resource
    private DictionaryConfig dictionaryConfig;
    @Resource
    private CommonCacheService commonCacheService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveMessageSendLog(Long userId, String mobileNumber, Long businessId, String variableParams, Long createId, MessageEnum messageType, String remark) {
        BasicMessage basicMessage = new BasicMessage();
        // code和content
        Map<String, String> enumInfo = this.getCodeAndContentByEnum(messageType);
        String templateCode = enumInfo.get("templateCode");
        String templateContent = enumInfo.get("templateContent");

        String errorMessage = null;
        if (SystemConstant.strNotNull(mobileNumber)){
            try {
                // 其他方法调用所传入的参数，必须校验有值
                this.checkData(userId, variableParams, messageType);

                // 短信提示系统是否启用配置验证
                SysConfig sysConfig = sysConfigService.getByKey("sys.message.enable");
                if (sysConfig == null) {
                    throw ExceptionResultEnum.ERROR.exception("未找到短信配置");
                }
                if (sysConfig.getConfigValue() == null) {
                    throw ExceptionResultEnum.ERROR.exception("短信消息提示启用开关未设置");
                }
                if (sysConfig.getConfigValue().equals("false")) {
                    throw ExceptionResultEnum.ERROR.exception("短信消息提示已关闭");
                }

                SendSmsResponse sendSmsResponse = sendSms(mobileNumber, templateCode, variableParams);

                if (sendSmsResponse.getCode() != null) {

                    basicMessage.setSendStatus(sendSmsResponse.getCode());
                    basicMessage.setSendResult(sendSmsResponse.getMessage());

                } else {
                    throw ExceptionResultEnum.ERROR.exception(sendSmsResponse.getMessage());
                }
            } catch (Exception e) {
                errorMessage = e.getMessage();
            } finally {
                // 传入的必填字段
                basicMessage.setUserId(userId);
                basicMessage.setMobileNumber(mobileNumber);
                basicMessage.setBusinessId(businessId);
                basicMessage.setVariableParams(variableParams);
                basicMessage.setCreateId(createId);
                basicMessage.setMessageType(messageType);
                basicMessage.setRemark(remark);
                basicMessage.setResendCount(0);

                // 经过处理的新字段
                basicMessage.setId(SystemConstant.getDbUuid());
                basicMessage.setBusinessOperate(messageType.getName());
                basicMessage.setCreateTime(System.currentTimeMillis());
                basicMessage.setTemplateCode(templateCode);
                if (errorMessage != null && errorMessage.length() > 0) {
                    basicMessage.setSendStatus("SYSTEM_ERROR");
                    basicMessage.setSendResult(errorMessage);
                }
                this.save(basicMessage);
            }
        }
    }

    /**
     * 发送短信
     * @param mobileNumber
     * @param templateCode
     * @param variableParams
     * @return
     * @throws ClientException
     */
    private SendSmsResponse sendSms(String mobileNumber, String templateCode, String variableParams) throws ClientException {
        System.setProperty("sun.net.client.defaultConnectTimeout", "180000");
        System.setProperty("sun.net.client.defaultReadTimeout", "18000");
        // 初始化ascClient需要的几个参数
        final String product = "Dysmsapi";// 短信API产品名称（短信产品名固定，无需修改）
        final String domain = "dysmsapi.aliyuncs.com";// 短信API产品域名（接口地址固定，无需修改）
        // 替换成你的AK
        final String accessKeyId = dictionaryConfig.smsDomain().getAliyunSMSKey();// 你的accessKeyId,参考本文档步骤2
        final String accessKeySecret = dictionaryConfig.smsDomain().getAliyunSMSSecret();// 你的accessKeySecret，参考本文档步骤2
        // 初始化ascClient,暂时不支持多region（请勿修改）
        IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
        DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);
        // 组装请求对象
        SendSmsRequest request = new SendSmsRequest();
        // 使用post提交
        request.setMethod(MethodType.POST);
        // 必填:待发送手机号。支持以逗号分隔的形式进行批量调用，批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式；发送国际/港澳台消息时，接收号码格式为00+国际区号+号码，如“0085200000000”
        request.setPhoneNumbers(mobileNumber);
        // 必填:短信签名-可在短信控制台中找到
        request.setSignName(dictionaryConfig.smsDomain().getAliyunSMSSignName());
        // 必填:短信模板-可在短信控制台中找到
        request.setTemplateCode(templateCode);
        // 可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
        // 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
        request.setTemplateParam(variableParams);
        // 可选-上行短信扩展码(扩展码字段控制在7位或以下，无特殊需求用户请忽略此字段)
        // request.setSmsUpExtendCode("90997");
        // 可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
//             request.setOutId("yourOutId");
        // 请求失败这里会抛ClientException异常

        SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
        return sendSmsResponse;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void noticeOfExamTaskAudit(List<Long> examTaskIdList, MessageEnum messageType, SysUser sysUser) {
        ExamTaskService examTaskService = SpringContextHolder.getBean(ExamTaskService.class);
        for (Long examTaskId : examTaskIdList) {
            ExamTask examTask = examTaskService.getById(examTaskId);
            if (Objects.nonNull(examTask)) {
                String courseName = examTask.getCourseName();
                String paperNumber = examTask.getPaperNumber();
                // 获取短信接收对象的信息
                Long userId = examTask.getUserId();
                SysUser user = commonCacheService.userCache(userId);
                if (Objects.nonNull(user)) {
                    String userName = user.getRealName();
                    String mobileNumber = user.getMobileNumber();

                    Map<String, String> jsonMap = new HashMap<>();
                    jsonMap.put("userName", userName);
                    jsonMap.put("courseName", courseName);
                    jsonMap.put("paperNumber", paperNumber);
                    String variableParams = JSON.toJSONString(jsonMap);
                    this.saveMessageSendLog(userId, mobileNumber, examTaskId, variableParams, sysUser.getId(), messageType, null);
                }
            }
        }

    }

    /**
     * 发送给命题老师-待办提醒
     *
     * @param examTasks
     */
    @Override
    public void sendNoticeTaskCreate(ExamTask... examTasks) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        // 指派命题老师的命题任务
        Map<Long, List<String>> collects = Arrays.stream(examTasks).filter(m -> m.getUserId() != null).collect(Collectors.groupingBy(ExamTask::getUserId, Collectors.mapping(m -> String.valueOf(m.getId()), Collectors.toList())));
        for (Map.Entry<Long, List<String>> longListEntry : collects.entrySet()) {
            Long userId = longListEntry.getKey();
            List<String> ids = longListEntry.getValue();
            SysUser user = commonCacheService.userCache(userId);
            if (Objects.nonNull(user)) {
                String userName = user.getRealName();
                String mobileNumber = user.getMobileNumber();

                Map<String, Object> jsonMap = new HashMap<>();
                jsonMap.put("userName", userName);
                jsonMap.put("count", ids.size());
                String variableParams = JSON.toJSONString(jsonMap);
                String remark = String.format("命题任务ID：%s", String.join(",", ids));
                this.saveMessageSendLog(userId, mobileNumber, null, variableParams, sysUser.getId(), MessageEnum.NOTICE_OF_EXAM_TASK_CREATED, remark);
            }
        }
    }

    @Override
    public void sendNoticeTaskAuditCreateOrReview(ExamTask examTask, MessageEnum messageType) {
        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
        // 考务老师(命题任务创建人)
        Long userId = examTask.getCreateId();
        SysUser user = commonCacheService.userCache(userId);
        if (Objects.nonNull(user)) {
            String userName = user.getRealName();
            String mobileNumber = user.getMobileNumber();

            Map<String, Object> jsonMap = new HashMap<>();
            jsonMap.put("userName", userName);
            StringJoiner sj = new StringJoiner("、").add(examTask.getCourseName()).add(examTask.getPaperNumber());
            jsonMap.put("courseNameAndPaperNumber", sj.toString());
            String variableParams = JSON.toJSONString(jsonMap);
            String remark = String.format("命题任务ID：%s", examTask.getId().toString());
            this.saveMessageSendLog(userId, mobileNumber, examTask.getId(), variableParams, sysUser.getId(), messageType, remark);
        }
    }

    @Override
    public void sendNoticeExpireOrOverdue(MessageEnum messageType, Long userId, List<String> ids) {
        // 考务老师(命题任务创建人)
        SysUser user = commonCacheService.userCache(userId);
        if (Objects.nonNull(user)) {
            String userName = user.getRealName();
            String mobileNumber = user.getMobileNumber();
            Map<String, Object> jsonMap = new HashMap<>();
            jsonMap.put("userName", userName);
            jsonMap.put("count", ids.size());
            String variableParams = JSON.toJSONString(jsonMap);
            String remark = String.format("命题任务ID：%s", String.join(",", ids));
            this.saveMessageSendLog(userId, mobileNumber, null, variableParams, null, messageType, remark);
        }
    }

    @Override
    public void resendSmsTask() {
        SysConfig sysConfig = sysConfigService.getByKey("sys.message.resendCount");
        if(sysConfig != null){
            Integer resendCount = StringUtils.isBlank(sysConfig.getConfigValue()) ? 0 : Integer.valueOf(sysConfig.getConfigValue());
            QueryWrapper<BasicMessage> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().ne(BasicMessage::getSendStatus, "OK").lt(BasicMessage::getResendCount, resendCount);
            List<BasicMessage> basicMessageList = this.list(queryWrapper);
            if (basicMessageList != null && basicMessageList.size() > 0) {
                for (BasicMessage basicMessage : basicMessageList) {
                    this.saveMessageResendLog(basicMessage);
                }
            }
        }
    }

    /**
     * 自动重发
     * @param basicMessage
     */
    private void saveMessageResendLog(BasicMessage basicMessage) {
        String errorMessage = null;
        try {
            SendSmsResponse sendSmsResponse = sendSms(basicMessage.getMobileNumber(), basicMessage.getTemplateCode(), basicMessage.getVariableParams());
            if (sendSmsResponse.getCode() != null) {
                basicMessage.setSendStatus(sendSmsResponse.getCode());
                basicMessage.setSendResult(sendSmsResponse.getMessage());
            } else {
                throw ExceptionResultEnum.ERROR.exception(sendSmsResponse.getMessage());
            }
        } catch (Exception e) {
            errorMessage = e.getMessage();
        } finally {
            // 重试次数+1
            basicMessage.setResendCount(basicMessage.getResendCount() + 1);
            // 经过处理的新字段
            if (errorMessage != null && errorMessage.length() > 0) {
                basicMessage.setSendStatus("SYSTEM_ERROR");
                basicMessage.setSendResult(errorMessage);
            }
            this.updateById(basicMessage);
        }
    }

    private void checkData(Object... objects) {
        for (Object object : objects) {
            if (Objects.isNull(object)) {
                throw ExceptionResultEnum.ERROR.exception("调用发送短信方法时必传参数缺失");
            } else if (object instanceof String) {
                String param = String.valueOf(object);
                if (param.length() == 0 || param.equals("null")) {
                    throw ExceptionResultEnum.ERROR.exception("调用发送短信方法时必传参数缺失");
                }
            } else if (object instanceof Long) {
                Long param = SystemConstant.convertIdToLong(String.valueOf(object));
                if (param == null || param == 0) {
                    throw ExceptionResultEnum.ERROR.exception("调用发送短信方法时必传参数缺失");
                }
            }
        }
    }

    private Map<String, String> getCodeAndContentByEnum(MessageEnum messageEnum) {
        Map<String, String> result = new HashMap<>();
        String templateContent = null;
        String templateCode = null;
        switch (messageEnum) {
            case NOTICE_OF_AUDIT_PASS:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAuditPassCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_AUDIT_NOT_PASS:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAuditNotPassCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_EXAM_TASK_CREATED:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSExamTaskCreatedCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_EXAM_TASK_WILL_EXPIRE:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSExamTaskWillExpireCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_EXAM_TASK_OVERDUE:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSExamTaskOverdueCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_ALLOCATION_WILL_EXPIRE:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAllocationWillExpireCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_ALLOCATION_OVERDUE:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAllocationOverdueCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_AUDIT_CREATED:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAuditCreatedCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_AUDIT_REVIEW:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAuditReviewCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_AUDIT_WILL_EXPIRE:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAuditWillExpireCode();
                templateContent = messageEnum.getTemplate();
                break;
            case NOTICE_OF_AUDIT_OVERDUE:
                templateCode = dictionaryConfig.smsDomain().getAliyunSMSAuditOverdueCode();
                templateContent = messageEnum.getTemplate();
                break;
        }
        result.put("templateContent", templateContent);
        result.put("templateCode", templateCode);
        return result;
    }
}
