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.FaultSubscriber;
import com.kym.entity.MpMsgTemplate;
import com.kym.service.FaultSubscriberService;
import com.kym.service.MpMsgTemplateService;
import com.kym.service.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.message.WxMpXmlMessage;
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;
    private final FaultSubscriberService faultSubscriberService;

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

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

    /**
     * 处理微信公众号通知
     *
     * @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);
            if (ret.containsKey("Encrypt")) {
                String timestamp = request.getParameter("timestamp");
                String nonce = request.getParameter("nonce");
                String msgSignature = request.getParameter("msg_signature");
                WxMpXmlMessage decrypted = WxMpXmlMessage.fromEncryptedXml(
                        xml, wxMpService.getWxMpConfigStorage(), timestamp, nonce, msgSignature);
                log.info("消息已解密: FromUser={}, MsgType={}, Event={}",
                        decrypted.getFromUser(), decrypted.getMsgType(), decrypted.getEvent());
                // 将解密后的字段回填到 ret map，后续逻辑无需改动
                ret.clear();
                ret.put("ToUserName", decrypted.getToUser());
                ret.put("FromUserName", decrypted.getFromUser());
                ret.put("CreateTime", String.valueOf(decrypted.getCreateTime()));
                ret.put("MsgType", decrypted.getMsgType());
                ret.put("Content", decrypted.getContent());
                ret.put("MsgId", decrypted.getMsgId());
                if (decrypted.getEvent() != null) {
                    ret.put("Event", decrypted.getEvent());
                    ret.put("EventKey", decrypted.getEventKey());
                }
            }
            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，关联小程序用户
                            try {
                                mpRelationService.bindMpUser(mpOpenid);
                            } catch (Exception e) {
                                log.error("绑定公众号用户失败: mpOpenid={}", mpOpenid, e);
                            }
                            // 处理带场景值的关注（扫码绑定故障通知）
                            handleSceneBinding(mpOpenid, ret, true);
                            break;
                        case "unsubscribe":
                            // 取消关注公众号
                            log.info("收到微信公众号取消关注通知: {}", mpOpenid);
                            mpRelationService.unbindMpUser(mpOpenid);
                            break;
                        case "SCAN":
                            // 扫描二维码（微信推送上来的事件是大写 SCAN）
                            log.info("收到微信公众号扫描二维码通知: {}", mpOpenid);
                            // 处理带场景值的扫码（扫码绑定/解绑故障通知）
                            handleSceneBinding(mpOpenid, ret, false);
                            break;
                        case "location":
                            // 上报地理位置
                            log.info("收到微信公众号上报地理位置通知: {}", mpOpenid);
                            break;
                        case "click":
                            // 点击菜单拉取消息
                            log.info("收到微信公众号点击菜单拉取消息通知: {}", mpOpenid);
                            break;
                        case "view":
                            // 点击菜单跳转链接
                            log.info("收到微信公众号点击菜单跳转链接通知: {}", mpOpenid);
                            break;
                    }
                }

                response.setContentType("text/html; charset=UTF-8");
                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);
        }
    }

    /**
     * 处理扫码绑定/解绑故障通知
     * scene_str 格式: stationId（直接使用站点ID作为场景值）
     *
     * @param mpOpenid  公众号openid
     * @param eventData 微信推送的XML解析后的Map
     * @param isSubscribe 是否为关注事件（关注事件的EventKey前缀为 qrscene_）
     */
    private void handleSceneBinding(String mpOpenid, Map<String, Object> eventData, boolean isSubscribe) {
        try {
            var eventKey = eventData.get("EventKey");
            if (eventKey == null) {
                return;
            }
            var eventKeyStr = eventKey.toString();
            if (CommUtil.isEmptyOrNull(eventKeyStr)) {
                return;
            }

            // 关注事件: qrscene_<scene_str>，扫码事件: <scene_str>
            String stationId;
            if (isSubscribe && eventKeyStr.startsWith("qrscene_")) {
                stationId = eventKeyStr.substring("qrscene_".length());
            } else {
                stationId = eventKeyStr;
            }

            if (CommUtil.isEmptyOrNull(stationId)) {
                return;
            }

            var result = faultSubscriberService.toggleSubscription(mpOpenid, stationId);
            log.info("故障通知订阅操作: openid={}, stationId={}, result={}", mpOpenid, stationId, result);

            // 绑定成功时更新微信昵称
            if ("绑定成功".equals(result)) {
                try {
                    var mpUser = wxMpService.getUserService().userInfo(mpOpenid, "zh_CN");
                    faultSubscriberService.lambdaUpdate()
                            .set(FaultSubscriber::getNickname, mpUser.getNickname())
                            .eq(FaultSubscriber::getOpenid, mpOpenid)
                            .eq(FaultSubscriber::getStationId, stationId)
                            .update();
                } catch (Exception e) {
                    log.warn("获取微信昵称失败: mpOpenid={}", mpOpenid, e);
                }
            }

            var stationName = com.kym.service.cache.KymCache.INSTANCE.getStationNameById(stationId);
            var displayName = stationName != null ? stationName : stationId;

            // 构造被动文本回复：交换收发方，设置文本消息内容
            var replyContent = result + "！\n站点：" + displayName + "\n" + ("绑定成功".equals(result) ? "您将收到该站点的设备故障提醒。" : "您将不再收到该站点的设备故障提醒。");
            var fromUser = eventData.get("FromUserName"); // 用户openid，回复时要变成 ToUserName
            var toUser = eventData.get("ToUserName");     // 公众号ID，回复时要变成 FromUserName
            eventData.put("ToUserName", fromUser);
            eventData.put("FromUserName", toUser);
            eventData.put("CreateTime", String.valueOf(System.currentTimeMillis() / 1000));
            eventData.put("MsgType", "text");
            eventData.put("Content", replyContent);
        } catch (Exception e) {
            log.error("处理场景绑定异常: mpOpenid={}", mpOpenid, e);
        }
    }

    /**
     * 获取模板消息的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;
    }

}
