package com.liquidnet.service.sweet.service.impl;

import com.liquidnet.service.sweet.handler.SubscribeHandler;
import com.liquidnet.service.sweet.handler.UnsubscribeHandler;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 公众号动作回调 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2021-07-31
 */
@Service
@Slf4j
public class SweetWechatCallbackServiceImpl {

    @Value("${liquidnet.wechat.service.zhengzai.appid}")
    private String appid;
    @Value("${liquidnet.wechat.service.zhengzai.secret}")
    private String secret;
    @Value("${liquidnet.wechat.service.zhengzai.token}")
    private String token;
    @Value("${liquidnet.wechat.service.zhengzai.aeskey}")
    private String aeskey;

    @Autowired
    private SubscribeHandler subscribeHandler;
    @Autowired
    private UnsubscribeHandler unsubscribeHandler;


    private WxMpConfigStorage wxMpConfig(String appId, String appSecret, String token, String aeskey) {
        WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
        config.setAppId(appId);
        config.setSecret(appSecret);
        config.setAesKey(aeskey);
        config.setToken(token);
        return config;
    }

    private WxMpService wxMpService(WxMpConfigStorage mpConfig) {
        WxMpService service = new WxMpServiceImpl();
        service.setWxMpConfigStorage(mpConfig);
        return service;
    }

    public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
        WxMpMessageRouter router = new WxMpMessageRouter(wxMpService);
        // 消息去重
        router.setMessageDuplicateChecker(new WxMessageInMemoryDuplicateChecker());

        // 关注事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.SUBSCRIBE)
                .handler(subscribeHandler)
                .end();

        // 取消关注事件
        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                .event(WxConsts.EventType.UNSUBSCRIBE)
                .handler(unsubscribeHandler)
                .end();

        return router;
    }

    public String record(String requestBody, String timestamp, String nonce, String encType, String msgSignature) {
        WxMpConfigStorage wxMpConfig = wxMpConfig(appid, secret, token, aeskey);
        WxMpService wxMpService = wxMpService(wxMpConfig);
        String out = null;
        if (encType == null || encType.isEmpty()) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            log.info("\n消息内容为：\n[{}] ", inMessage.toString());
            WxMpXmlOutMessage outMessage = messageRouter(wxMpService).route(inMessage);
            if (outMessage == null) {
                // 假如服务器无法保证在五秒内处理并回复，可以直接回复空串，微信服务器不会对此作任何处理，并且不会发起重试
                return "";
            }
            out = outMessage.toXml();
        } else if ("aes".equalsIgnoreCase(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(),
                    timestamp, nonce, msgSignature);
            log.info("\n消息解密后内容为：\n[{}] ", inMessage.toString());
            WxMpXmlOutMessage outMessage = messageRouter(wxMpService).route(inMessage);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage());
        }
        log.info("\n组装回复信息：[{}]", out);
        return out;
    }
}
