package cn.novalon.gym.manage.member.handler;

import cn.novalon.gym.manage.member.config.WechatProperties;
import cn.novalon.gym.manage.member.service.WechatOfficialService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;

/**
 * 微信公众号事件处理器
 * 
 * @author 付嘉
 * @date 2026-05-01
 */

@Slf4j
@Component
@RequiredArgsConstructor
public class WechatOfficialEventHandler {

    private final WechatOfficialService wechatOfficialService;
    private final WechatProperties wechatProperties;

    /**
     * 处理微信公众号事件
     * 
     * 请求格式:XML
     * 响应格式:success 或 回复消息内容
     */
    public Mono<ServerResponse> handleEvent(ServerRequest request) {
        return request.bodyToMono(String.class)
                .flatMap(xmlBody -> {
                    log.info("收到微信公众号事件 {}", xmlBody);
                    
                    String openId = extractOpenId(xmlBody);
                    String event = extractEvent(xmlBody);

                    if (openId == null || event == null) {
                        log.error("无法解析微信公众号事件");
                        return ServerResponse.badRequest().bodyValue("error");
                    }

                    log.info("处理事件 openId={}, event={}", openId, event);

                    // 根据事件类型处理
                    if ("subscribe".equals(event)) {
                        return wechatOfficialService.handleSubscribeEvent(openId)
                                .then(ServerResponse.ok()
                                        .contentType(MediaType.TEXT_PLAIN)
                                        .bodyValue("success"));
                    } else if ("unsubscribe".equals(event)) {
                        return wechatOfficialService.handleUnsubscribeEvent(openId)
                                .then(ServerResponse.ok()
                                        .contentType(MediaType.TEXT_PLAIN)
                                        .bodyValue("success"));
                    } else {
                        log.warn("未知事件类型: {}", event);
                        return ServerResponse.ok()
                                .contentType(MediaType.TEXT_PLAIN)
                                .bodyValue("success");
                    }
                })
                .onErrorResume(e -> {
                    log.error("处理微信公众号事件失败", e);
                    return ServerResponse.ok()
                            .contentType(MediaType.TEXT_PLAIN)
                            .bodyValue("success"); // 即使处理失败也返回success避免微信重试
                });
    }

    /**
     * 验证微信公众号签名
     * 
     * GET请求用于验证服务器地址
     */
    public Mono<ServerResponse> verifySignature(ServerRequest request) {
        String signature = request.queryParam("signature").orElse("");
        String timestamp = request.queryParam("timestamp").orElse("");
        String nonce = request.queryParam("nonce").orElse("");
        String echostr = request.queryParam("echostr").orElse("");

        log.info("========== 微信公众号签名验证 ==========");
        log.info("收到的参数:");
        log.info("  signature: {}", signature);
        log.info("  timestamp: {}", timestamp);
        log.info("  nonce: {}", nonce);
        log.info("  echostr: {}", echostr);

        // 获取配置的Token
        String token = wechatProperties.getMp().getToken();
        log.info("配置的Token: {}", token);

        // 验证签名
        if (checkSignature(signature, timestamp, nonce, token)) {
            log.info("签名验证成功，返回echostr: {}", echostr);
            log.info("========== 微信公众号签名验证结束 ==========");
            return ServerResponse.ok()
                    .contentType(MediaType.TEXT_PLAIN)
                    .bodyValue(echostr);
        } else {
            log.warn("签名验证失败");
            log.info("========== 微信公众号签名验证结束 ==========");
            return ServerResponse.badRequest()
                    .contentType(MediaType.TEXT_PLAIN)
                    .bodyValue("error");
        }
    }

    /**
     * 验证签名
     * 
     * @param signature 微信加密签名
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @param token     Token
     * @return 是否验证通过
     */
    private boolean checkSignature(String signature, String timestamp, String nonce, String token) {
        // 1. 将token、timestamp、nonce三个参数进行字典序排序
        String[] arr = new String[]{token, timestamp, nonce};
        Arrays.sort(arr);

        // 2. 将三个参数字符串拼接成一个字符串
        StringBuilder sb = new StringBuilder();
        for (String str : arr) {
            sb.append(str);
        }

        // 3. 将拼接后的字符串进行sha1加密
        String encrypted = sha1(sb.toString());
        log.debug("计算的签名 {}", encrypted);

        // 4. 将加密后的字符串与signature对比
        return encrypted != null && encrypted.equalsIgnoreCase(signature);
    }

    /**
     * SHA1加密
     * 
     * @param str 待加密字符串
     * @return 加密后字符串
     */
    private String sha1(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(str.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : digest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            log.error("SHA1加密失败", e);
            return null;
        }
    }

    /**
     * 从XML中提取OpenID
     */
    private String extractOpenId(String xml) {
        int start = xml.indexOf("<FromUserName>");
        int end = xml.indexOf("</FromUserName>");
        if (start != -1 && end != -1) {
            String value = xml.substring(start + 14, end);
            // 去除 CDATA 标记
            return cleanCdata(value);
        }
        return null;
    }
    
    /**
     * 从XML中获取事件类型
     */
    private String extractEvent(String xml) {
        int start = xml.indexOf("<Event>");
        int end = xml.indexOf("</Event>");
        if (start != -1 && end != -1) {
            String value = xml.substring(start + 7, end);
            // 去除 CDATA 标记
            return cleanCdata(value);
        }
        return null;
    }
    
    /**
     * 清理 CDATA 标记
     * 例如: <![CDATA[subscribe]]> -> subscribe
     */
    private String cleanCdata(String value) {
        if (value == null) {
            return null;
        }
        // 去除前后空白
        value = value.trim();
        // 提取 CDATA 中间内容
        // 格式: <![CDATA[xxx]]>
        if (value.startsWith("<![CDATA[") && value.endsWith("]]>")) {
            return value.substring(9, value.length() - 3);
        }
        return value;
    }
}
