package com.kym.service.wechat.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.kym.common.utils.CommUtil;
import com.kym.entity.miniapp.MpMsgTemplate;
import com.kym.entity.miniapp.MpRelation;
import com.kym.service.miniapp.MpMsgTemplateService;
import com.kym.service.miniapp.impl.MpRelationServiceImpl;
import com.kym.service.wechat.WeixinMPService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;

/**
 * 微信公众号服务
 *
 * @author skyline
 */
@Service
@Slf4j
public class WeixinMPServiceImpl implements WeixinMPService {

    private final MpMsgTemplateService mpMsgTemplateService;
    private final MpRelationServiceImpl mpRelationService;
    private final WxMpService wxMpService;

    /**
     * 小程序appid
     */
    @Value("${wechat.miniapp.appid}")
    public String appid;

    public WeixinMPServiceImpl(MpMsgTemplateService mpMsgTemplateService, MpRelationServiceImpl mpRelationService, WxMpService wxMpService) {
        this.mpMsgTemplateService = mpMsgTemplateService;
        this.mpRelationService = mpRelationService;
        this.wxMpService = wxMpService;
    }

    /**
     * 处理微信公众号通知
     *
     * @param request
     * @param response
     */
    @Override
    public void handleWxMPNotify(HttpServletRequest request, HttpServletResponse response) {
        String result = "";
        try {
            String xml = IoUtil.read(request.getInputStream(), CharsetUtil.CHARSET_UTF_8);
            log.info("收到微信公众号通知：xml message:{}", xml);

            Map<String, Object> ret = XmlUtil.xmlToMap(xml);
            log.info("收到微信公众号通知：map message:{}", ret);

            if (!CommUtil.isEmptyOrNull(ret)) {
                // 公众号用户的openid
                var mpOpenid = ret.get("FromUserName").toString();
                // 事件类型
                var msgType = ret.get("MsgType").toString();
                var event = ret.get("Event").toString();
                if ("event".equals(msgType) && CommUtil.isNotEmptyAndNull(event)) {
                    switch (event) {
                        case "subscribe":
                            // 关注公众号
                            log.info("收到微信公众号关注通知: {}", mpOpenid);
                            // 通过openid获取用户信息中的unionid，关联小程序用户
                            mpRelationService.bindMpUser(mpOpenid);
                            break;
                        case "unsubscribe":
                            // 取消关注公众号
                            log.info("收到微信公众号取消关注通知: {}", mpOpenid);
                            // 更新用户的关注状态
                            mpRelationService.lambdaUpdate().set(MpRelation::getSubscribe, false).eq(MpRelation::getMpOpenid, mpOpenid).update();
                            break;
                        case "scan":
                            // 扫描二维码
                            log.info("收到微信公众号扫描二维码通知: {}", mpOpenid);
                            break;
                        case "location":
                            // 上报地理位置
                            log.info("收到微信公众号上报地理位置通知: {}", mpOpenid);
                            break;
                        case "click":
                            // 点击菜单拉取消息
                            log.info("收到微信公众号点击菜单拉取消息通知: {}", mpOpenid);
                            break;
                        case "view":
                            // 点击菜单跳转链接
                            log.info("收到微信公众号点击菜单跳转链接通知: {}", mpOpenid);
                            break;
                    }
                }

                response.setContentType("text/html");
                result = XmlUtil.mapToXmlStr(ret);
            }
        } catch (Exception e) {
            log.error("微信公众号消息处理异常", e);
        } finally {
            try {
                log.info("微信公众号通知响应: {}", result);
                PrintWriter writer = response.getWriter();
                writer.print(result);
                writer.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }

    }

    @Override
    @Async
    public void sendMPTemplateMessage(String mpOpenid, String templateTitle, List<String> dataList) throws WxErrorException {
        var template = getMPMsgTemplate(templateTitle);
        CommUtil.asserts(template != null, "模板消息不存在");
        var templateKeys = getMPMsgTemplateKeys(template);
        if (dataList.size() == templateKeys.length) {
            WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
                    .toUser(mpOpenid)
                    .templateId(template.getTemplateId())
                    .url("")
                    .build();
            for (int i = 0; i < templateKeys.length; i++) {
                templateMessage.addData(new WxMpTemplateData(templateKeys[i], dataList.get(i)));
            }
            if (CommUtil.isNotEmptyAndNull(template.getPagePath())) {
                templateMessage.setMiniProgram(new WxMpTemplateMessage.MiniProgram(appid, template.getPagePath(), false));
            }
            wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
        }
    }

    /**
     * 获取模板消息的key
     *
     * @param template
     * @return
     */
    private String[] getMPMsgTemplateKeys(MpMsgTemplate template) {
        return StrUtil.removeAll(template.getContent().replace(".DATA", ""), '{', '}').split(",");
    }

    /**
     * 获取模板消息
     *
     * @param title
     * @return
     */
    private MpMsgTemplate getMPMsgTemplate(String title) {
        return mpMsgTemplateService.lambdaQuery().eq(MpMsgTemplate::getTitle, title).oneOpt()
                .isPresent() ? mpMsgTemplateService.lambdaQuery().eq(MpMsgTemplate::getTitle, title).one() : null;
    }

}
