package cn.ibizlab.core.extensions.service;

import cn.ibizlab.core.extensions.domain.Msg;
import cn.ibizlab.core.extensions.domain.Template;
import cn.ibizlab.core.notify.domain.MsgOpenAccess;
import cn.ibizlab.core.notify.domain.MsgTemplate;
import cn.ibizlab.core.notify.domain.MsgUserAccount;
import cn.ibizlab.core.notify.service.IMsgOpenAccessService;
import cn.ibizlab.core.notify.service.IMsgTemplateService;
import cn.ibizlab.core.notify.service.IMsgUserAccountService;
import cn.ibizlab.util.errors.BadRequestAlertException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dm.model.v20151123.SingleSendMailRequest;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import com.dingtalk.api.request.OapiWorkrecordAddRequest;
import com.dingtalk.api.request.OapiWorkrecordUpdateRequest;
import com.dingtalk.api.response.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import java.sql.Timestamp;
import java.util.*;

@Service
@Slf4j
public class NotifyCoreService {

    /**
     * 钉钉获取token api
     */
    private static final String dingTalkTokenApi="https://oapi.dingtalk.com/gettoken";
    /**
     * 钉钉发送消息api
     */
    private static final String dingTalkSendMsgApi="https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2";
    /**
     * 钉钉创建待办api
     */
    private static final String dingTalkCreateWorkRecordApi="https://oapi.dingtalk.com/topapi/workrecord/add";
    /**
     * 钉钉完成待办api
     */
    private static final String dingTalkFinishWorkRecordApi="https://oapi.dingtalk.com/topapi/workrecord/update";
    /**
     * 微信获取token api
     */
    private  final String weChatGetTokenApi="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    /**
     * 微信发送消息api
     */
    private  final String weChatSendMsgApi="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
    /**
     * 阿里短信服务域名
     */
    private static final String aliSmsDomain="dysmsapi.aliyuncs.com";
    /**
     * 阿里短信服务单处理行为
     */
    private static final String aliSendSmsAction="SendSms";
    /**
     * 阿里短信服务单处理行为
     */
    private static final String aliSendSmsVersion="2017-05-25";

    /**
     * 消息类型
     */
    enum MsgType{
        dingtalk,
        aliyun,
        wechat,
    }

    /**
     * 平台消息类型代码表
     */
    private static Map<Integer,String> templateType = new HashMap(){{
        put(2,"aliyun-email");
        put(4,"aliyun-sms");
        put(32,"wechat-msg");
        put(64,"dingtalk-msg");
    }};

    private static DingTalkClient sendMsgClient;
    private static DingTalkClient createWorkRecordClient;
    private static DingTalkClient finishWorkRecordClient;
    private static DingTalkClient getTokenClient;
    static {
        getTokenClient = new DefaultDingTalkClient(dingTalkTokenApi);
        sendMsgClient = new DefaultDingTalkClient(dingTalkSendMsgApi);
        createWorkRecordClient=new DefaultDingTalkClient(dingTalkCreateWorkRecordApi);
        finishWorkRecordClient = new DefaultDingTalkClient(dingTalkFinishWorkRecordApi);
    }

    @Autowired
    @Lazy
    RestTemplate restTemplate;
    @Autowired
    @Lazy
    IMsgOpenAccessService openAccessService;
    @Autowired
    @Lazy
    IMsgTemplateService templateService;
    @Autowired
    @Lazy
    IMsgUserAccountService userAccountService;

    @Autowired
    @Lazy
    NotifyCoreService notifyCoreService;

    /**
     * 获取钉钉token
     * @param openAccess
     * @return
     */
    @SneakyThrows
    public String getDingTalkToken(MsgOpenAccess openAccess){
        String accessKey=openAccess.getAccessKey();
        String accessSecret=openAccess.getSecretKey();
        OapiGettokenRequest req = new OapiGettokenRequest();
        req.setAppkey(accessKey);
        req.setAppsecret(accessSecret);
        req.setHttpMethod("GET");
        OapiGettokenResponse rsp = getTokenClient.execute(req);
        getResult(rsp.getBody());
        return rsp.getBody();
    }

    /**
     * 获取微信token
     * @param openAccess
     * @return
     */
    public String getWeChatToken(MsgOpenAccess openAccess){
        String accessKey=openAccess.getAccessKey();
        String accessSecret=openAccess.getSecretKey();
        ResponseEntity<String> result = restTemplate.getForEntity(String.format(weChatGetTokenApi,accessKey,accessSecret), String.class);
        getResult(result.getBody());
        return result.getBody();
    }

    /**
     * 发送钉钉消息
     * @param msg
     * @param tid
     */
    @SneakyThrows
    private boolean sendDingTalkMsg(Msg msg,String tid){
        String authUserId=getAuthUserId(msg.getUserids(),MsgType.dingtalk);
        MsgTemplate template=templateService.getById(tid);
        MsgOpenAccess openAccess =template.getOpenaccess();
        OapiMessageCorpconversationAsyncsendV2Request req = new OapiMessageCorpconversationAsyncsendV2Request();
        req.setAgentId(Long.parseLong(template.getTemplateId()));
        req.setUseridList(authUserId);
        OapiMessageCorpconversationAsyncsendV2Request.Msg dingtalkMsg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        dingtalkMsg.setMsgtype("text");
        OapiMessageCorpconversationAsyncsendV2Request.Text obj2 = new OapiMessageCorpconversationAsyncsendV2Request.Text();
        obj2.setContent(StringUtils.isEmpty(msg.getContent())?template.getContent():msg.getContent());
        dingtalkMsg.setText(obj2);
        req.setMsg(dingtalkMsg);
        OapiMessageCorpconversationAsyncsendV2Response rsp = sendMsgClient.execute(req, openAccess.getAccessToken());
        getResult(rsp.getBody());
        return true;
    }

    /**
     * 发送阿里短信
     * @param tid
     * @param userId
     */
    @SneakyThrows
    private boolean sendAliSms(String tid,String userId,Map templParams){
        String authUserId=getAuthUserId(userId,MsgType.aliyun);
        MsgTemplate template=templateService.getById(tid);
        MsgOpenAccess openAccess =template.getOpenaccess();
        String region=openAccess.getRegionId();
        String accessKey=openAccess.getAccessKey();
        String accessSecret=openAccess.getSecretKey();
        DefaultProfile profile = DefaultProfile.getProfile(StringUtils.isEmpty(region)?"cn-hangzhou":region, accessKey, accessSecret);
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        request.setSysDomain(aliSmsDomain);

        request.setSysAction(aliSendSmsAction);
        request.putQueryParameter("RegionId", region);
        request.putQueryParameter("PhoneNumbers", authUserId);
        request.putQueryParameter("SignName", template.getTemplateName());
        request.putQueryParameter("Version", aliSendSmsVersion);
        request.putQueryParameter("TemplateCode", template.getTemplateId());
        request.putQueryParameter("TemplateParam", templParams.toString());
        CommonResponse response = client.getCommonResponse(request);
        log.info(String.format("成功向用户[%s]发送一条短信",authUserId));
        return true;
    }

    /**
     * 发送阿里邮件
     * @param tid
     * @param userId
     */
    @SneakyThrows
    private boolean sendAliEMail(String tid,String userId){

        String authUserId=getAuthUserId(userId,MsgType.aliyun);
        MsgTemplate template=templateService.getById(tid);
        MsgOpenAccess openAccess =template.getOpenaccess();
        String region=openAccess.getRegionId();
        String accessKey=openAccess.getAccessKey();
        String accessSecret=openAccess.getSecretKey();
        DefaultProfile profile = DefaultProfile.getProfile(StringUtils.isEmpty(region)?"cn-hangzhou":region, accessKey, accessSecret);
        IAcsClient client = new DefaultAcsClient(profile);

        SingleSendMailRequest request = new SingleSendMailRequest();
        request.setAccountName(template.getTemplateId());
        request.setAddressType(1);
        request.setReplyToAddress(false);
        request.setToAddress(authUserId);
        request.setSubject(template.getTemplateName());
        request.setHtmlBody(template.getContent());
        client.getAcsResponse(request);
        log.info(String.format("成功向用户[%s]发送一条邮件",authUserId));
        return true;
    }


    /**
     * 发送微信消息
     * @param tid
     * @param userId
     */
    private boolean sendWeChatMsg(String tid,String userId,Map templParams){
        String authUserId=getAuthUserId(userId,MsgType.wechat);
        MsgTemplate template=templateService.getById(tid);
        MsgOpenAccess openAccess =template.getOpenaccess();
        Map paramMap=new HashMap();
        paramMap.put("template_id",template.getTemplateId());
        paramMap.put("touser",authUserId);
        paramMap.put("data",templParams);

        JSONObject obj=new JSONObject();
        obj.put("value","birdle test");
        templParams.put("result",obj);
        HttpEntity request = new HttpEntity(paramMap);
        ResponseEntity<String> result = restTemplate.postForEntity(String.format(weChatSendMsgApi,openAccess.getAccessToken()), request , String.class);
        getResult(result.getBody());
        return true;
    }

    /**
     * 获取第三方用户标识
     * @param userId
     * @return
     */
    private String getAuthUserId(String userId,MsgType type){
        QueryWrapper<MsgUserAccount> qw =new QueryWrapper();
        qw.eq("identity_type",type);
        if(userId.contains(",")){
            qw.in("userid",userId.split(","));
            List<MsgUserAccount> userAccounts =userAccountService.list(qw);
            if(userAccounts==null || userAccounts.size()==0)
                throw new BadRequestAlertException("未能获取到授权用户","NotifyCoreService","sendDingTalkMsg");

            StringBuffer authUserIds=new StringBuffer();
            for(MsgUserAccount userAccount: userAccounts){
                if(StringUtils.isEmpty(authUserIds.toString())){
                    authUserIds.append(userAccount.getIdentifier());
                }else{
                    authUserIds.append(","+userAccount.getIdentifier());
                }
            }
            return authUserIds.toString();
        }else{
            qw.eq("userid",userId);
            MsgUserAccount userAccount =userAccountService.getOne(qw);
            if(userAccount==null || StringUtils.isEmpty(userAccount.getIdentifier()))
                throw new BadRequestAlertException("未能获取到授权用户","NotifyCoreService","sendDingTalkMsg");
            return userAccount.getIdentifier();
        }
    }

    /**
     * 解析接口返回结果
     * @param result
     */
    private void getResult(String result){
        JSONObject resultJo = JSONObject.parseObject(result);
        if(resultJo.containsKey("errcode") && resultJo.getInteger("errcode")!=0)
            throw new BadRequestAlertException(String.format("发送消息失败,%s",resultJo.toString()),"","");
        else
            log.info("发送消息成功");
    }

    /**
     * 设置新token及过期时间
     * @param openAccess
     * @return
     */
    public MsgOpenAccess refreshToken(MsgOpenAccess openAccess){
        String tempToken=null;
        switch (openAccess.getOpenType()){
            case "wechat":
                tempToken=getWeChatToken(openAccess);
                break;
            case "dingtalk":
                tempToken=getDingTalkToken(openAccess);
                break;
        }
        if(!StringUtils.isEmpty(tempToken)){
            JSONObject objToken = JSONObject.parseObject(tempToken);
            if(objToken!=null){
                Calendar calendar =Calendar.getInstance();
                calendar.setTime(new Date());
                calendar.add(Calendar.SECOND,Integer.parseInt(objToken.getString("expires_in")));
                Date date = calendar.getTime();
                openAccess.setAccessToken(objToken.getString("access_token"));
                openAccess.setExpiresTime(new Timestamp(date.getTime()));
            }
        }
        return openAccess;
    }

    /**
     * 创建模板
     * @param template
     * @return
     */
    public boolean createMsgTemplate(Template template){
        JSONArray templates = template.getTemplate();
        for(int a=0;a<templates.size();a++){
            List<MsgOpenAccess> openAccesses=new ArrayList<>();
            List<MsgTemplate> msgTemplates=new ArrayList<>();
            MsgTemplate msgTempl =JSONObject.toJavaObject(templates.getJSONObject(a),MsgTemplate.class);
            boolean isExist=checkMsgTemplate(msgTempl);
            if(!isExist){
                //rt预置消息类型
                Collection<String> templTypes = templateType.values();
                //创建开放平台
                for(String openAccessType: getOpenAccessType(templTypes)){
                    String openAccessId=String.format("%s-%s",msgTempl.getTid(),openAccessType);
                    MsgOpenAccess openAccess=openAccessService.getById(openAccessId);
                    if(openAccess==null)
                    {
                        openAccess=new MsgOpenAccess();
                        openAccess.setId(openAccessId);
                        openAccess.setName(openAccessType);
                        openAccess.setDisabled(0);
                        openAccess.setOpenType(openAccessType);
                        openAccesses.add(openAccess);
                    }
                }
                //创建消息模板
                for(String templateType : templTypes){
                    MsgTemplate msgTemplate=new MsgTemplate();
                    String openAccessType=templateType.contains("-")?templateType.substring(0,templateType.indexOf("-")):templateType;
                    msgTemplate.setAccessId(String.format("%s-%s",msgTempl.getTid(),openAccessType));
                    msgTemplate.setOpenType(openAccessType);
                    msgTemplate.setTemplateType(templateType);
                    msgTemplate.setTid(String.format("%s-%s",msgTempl.getTid(),templateType));
                    msgTemplate.setTemplateName(msgTempl.getTemplateName());
                    msgTemplate.setContent(msgTempl.getContent());
                    msgTemplates.add(msgTemplate);
                }
                if(openAccesses.size()>0)
                    openAccessService.createBatch(openAccesses);
                if(msgTemplates.size()>0)
                    templateService.createBatch(msgTemplates);
            }
       }
        return true;
    }

    /**
     * 检查模板是否存在
     * @param msgTemplate
     * @return
     */
    private boolean checkMsgTemplate(MsgTemplate msgTemplate) {
        boolean isExist=false;
        if(!ObjectUtils.isEmpty(msgTemplate.getTid())){
            QueryWrapper<MsgOpenAccess> qw=new QueryWrapper();
            qw.likeRight("accessid",msgTemplate.getTid());
             if(openAccessService.count(qw)>0){
                 isExist=true;
                 log.info(String.format("消息模板[%s]已存在，忽略发布!",msgTemplate.getTemplateName()));
             }
        }
        return isExist;
    }

    /**
     * 获取开放平台
     * @param templTypes
     * @return
     */
    private List<String> getOpenAccessType(Collection<String> templTypes){
        List openAccess=new ArrayList();
        if(templTypes.size()==0)
            return openAccess;

        for(String template:templTypes){
            if(template.contains("-")){
                String openAccessType = template.substring(0,template.indexOf("-"));
                if(!openAccess.contains(openAccessType)){
                    openAccess.add(openAccessType);
                }
            }
        }
        return openAccess;
    }

    /**
     * 发送消息
     * @param msg
     * @return
     */
    public boolean sendMsg(Msg msg){
        String  templateId =msg.getTemplateid();
        String userIds=msg.getUserids();
        Map templParams=msg.getTemplparams();
        for(String msgType:getMsgType(msg.getMsgtypes())){
            MsgTemplate msgTemplate=templateService.getById(templateId+"-"+msgType);
            if(msgTemplate==null){
                throw new BadRequestAlertException(String.format("发送消息失败，无法获取到[%s]对应的rt模板",templateId),"","");
            }
            else{
                switch(msgType){
                    case "aliyun-sms":
                        sendAliSms(msgTemplate.getTid(),userIds,templParams==null?null:JSONObject.parseObject(JSON.toJSONString(templParams)));
                        break;
                    case "aliyun-email":
                        sendAliEMail(msgTemplate.getTid(),userIds);
                        break;
                    case "dingtalk-msg":
                        sendDingTalkMsg(msg,msgTemplate.getTid());
                        break;
                    case "wechat-msg":
                        sendWeChatMsg(msgTemplate.getTid(),userIds,getWechatMsgTemplParams(templParams));
                        break;
                    default:
                        throw new BadRequestAlertException(String.format("发送消息失败，尚未提供[%s]类型对应的消息服务",msgType),"","");
                }
            }
        }
        return true;
    }

    /**
     * 获取微信消息模板参数
     * @param templParams
     * @return
     */
    private JSONObject getWechatMsgTemplParams(Map<String, Object> templParams){

        if(templParams==null)
          return null;

        JSONObject params=new JSONObject();
        for (Map.Entry<String, Object> entry : templParams.entrySet()) {
            JSONObject param=new JSONObject();
            param.put("value",entry.getValue());
            params.put(entry.getKey(),param);
        }
        return params;
    }

    /**
     * 解析数值代码表获取消息类型
     * @param msgType
     * @return
     */
    private List<String> getMsgType(int msgType){
        List<String> useMsg=new ArrayList<>();
        for (Map.Entry<Integer,String> msg : templateType.entrySet()){
             if((msgType & msg.getKey()) == msg.getKey()){
                 useMsg.add(msg.getValue());
             }
        }
        return useMsg;
    }

    /**
     * 发送钉钉链接消息
     * @return
     */
    @SneakyThrows
    public Long sendLinkMsg(Msg msg){
        String authUserId=getAuthUserId(msg.getUserids(),MsgType.dingtalk);
        MsgTemplate template=templateService.getById(msg.getTemplateid());
        if(template==null){
            throw new BadRequestAlertException(String.format("发送链接消息失败，无法获取到[%s]对应的rt模板",msg.getTemplateid()),"","");
        }
        MsgOpenAccess openAccess =template.getOpenaccess();
        if (openAccess == null){
            openAccess = openAccessService.get(template.getAccessId());
        }
        OapiMessageCorpconversationAsyncsendV2Request req = new OapiMessageCorpconversationAsyncsendV2Request();
        req.setAgentId(Long.parseLong(template.getTemplateId()));
        req.setUseridList(authUserId);
        OapiMessageCorpconversationAsyncsendV2Request.Msg dingtalkMsg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        dingtalkMsg.setMsgtype("link");
        dingtalkMsg.setLink(new OapiMessageCorpconversationAsyncsendV2Request.Link());
        dingtalkMsg.getLink().setTitle(StringUtils.isEmpty(msg.getTitle())?template.getTemplateName():msg.getTitle());
        dingtalkMsg.getLink().setText(StringUtils.isEmpty(msg.getContent())?template.getContent():msg.getContent());
        dingtalkMsg.getLink().setMessageUrl(StringUtils.isEmpty(msg.getUrl())?template.getTemplateUrl():msg.getUrl());
        dingtalkMsg.getLink().setPicUrl("test");
        req.setMsg(dingtalkMsg);
        OapiMessageCorpconversationAsyncsendV2Response rsp = sendMsgClient.execute(req, openAccess.getAccessToken());
        getResult(rsp.getBody());
        return rsp.getTaskId();
    }

    /**
     * 建立待办
     * @param msg
     * @return
     */
    @SneakyThrows
    public Boolean createWorkRecord(Msg msg){
        String authUserId=getAuthUserId(msg.getUserids(),MsgType.dingtalk);
        MsgTemplate template=templateService.getById(msg.getTemplateid());
        if(template==null){
            throw new BadRequestAlertException(String.format("创建待办失败，无法获取到[%s]对应的rt模板",msg.getTemplateid()),"","");
        }
        MsgOpenAccess openAccess =template.getOpenaccess();
        if (openAccess == null){
            openAccess = openAccessService.get(template.getAccessId());
        }
        notifyCoreService.createWorkRecordByUserIds(authUserId,msg,template,openAccess);
        return true;
    }

    /**
     * 异步创建待办
     * @param userIds
     * @param msg
     * @param template
     * @param openAccess
     */
    @Async
    @SneakyThrows
    public void createWorkRecordByUserIds(String userIds,Msg msg,MsgTemplate template,MsgOpenAccess openAccess){
        for(String userId:userIds.split(",")){
            OapiWorkrecordAddRequest req = new OapiWorkrecordAddRequest();
            req.setUserid(userId);
            req.setCreateTime(new Date().getTime());
            req.setTitle(StringUtils.isEmpty(msg.getTitle())?template.getTemplateName():msg.getTitle());
            req.setUrl(StringUtils.isEmpty(msg.getUrl())?template.getTemplateUrl():msg.getUrl());
            List<OapiWorkrecordAddRequest.FormItemVo> list2 = new ArrayList<>();
            OapiWorkrecordAddRequest.FormItemVo obj3 = new OapiWorkrecordAddRequest.FormItemVo();
            list2.add(obj3);
            obj3.setTitle(StringUtils.isEmpty(msg.getTitle())?template.getTemplateName():msg.getTitle());
            obj3.setContent(StringUtils.isEmpty(msg.getContent())?template.getContent():msg.getContent());
            req.setFormItemList(list2);
            OapiWorkrecordAddResponse rsp = createWorkRecordClient.execute(req, openAccess.getAccessToken());
            getResult(rsp.getBody());
        }
    }

    /**
     * 完成待办
     * @param msg
     * @return
     */
    @SneakyThrows
    public boolean finishWorkRecord(Msg msg){
        String authUserId=getAuthUserId(msg.getUserids(),MsgType.dingtalk);
        MsgTemplate template=templateService.getById(msg.getTemplateid());
        if(template==null){
            throw new BadRequestAlertException(String.format("完成待办失败，无法获取到[%s]对应的rt模板",msg.getTemplateid()),"","");
        }
        MsgOpenAccess openAccess =template.getOpenaccess();
        OapiWorkrecordUpdateRequest req = new OapiWorkrecordUpdateRequest();
        req.setUserid(authUserId);
        req.setRecordId(msg.getRecordid());
        OapiWorkrecordUpdateResponse rsp = finishWorkRecordClient.execute(req, openAccess.getAccessToken());
        getResult(rsp.getBody());
        return rsp.getResult();
    }

}
