package com.dora.controller;

import com.dora.common.Result;
import com.dora.dto.AuthDto;
import com.dora.entity.User;
import com.dora.service.UserService;
import com.dora.service.WechatEventHandlerService;
import com.dora.util.JwtUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
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.result.WxMpQrCodeTicket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 微信公众号控制器
 * 处理微信公众号相关的接口，包括Token验证、扫码登录等
 * 
 * @author dora
 * @date 2024/12/01
 */
@Slf4j
@RestController
@RequestMapping("/user/wechat")
@Tag(name = "微信公众号", description = "微信公众号相关接口")
public class WeChatMpController {

    @Autowired
    private WxMpService wxMpService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;
    
    @Autowired
    private WechatEventHandlerService wechatEventHandlerService;

    @Value("${wx.mp.token}")
    private String token;
    
    private static final String WX_LOGIN_SCENE_PREFIX = "wx_login_scene:";
    private static final String WX_BIND_SCENE_PREFIX = "wx_bind_scene:";



    /**
     * 微信服务器验证Token接口 - 专用验证路径
     * 这个接口专门用于微信公众号后台验证服务器配置
     * 通过nginx反向代理将根路径请求转发到此路径
     * 
     * 验证流程：
     * 1. 微信服务器发送GET请求到根路径
     * 2. nginx将请求代理到 /user/wechat/verify
     * 3. 携带signature、echostr、timestamp、nonce四个参数
     * 4. 服务器验证signature是否正确
     * 5. 如果正确，返回echostr参数的值
     * 
     * @param signature 微信加密签名
     * @param echostr 随机字符串
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @param response HTTP响应对象
     */
    @GetMapping("/verify")
    @Operation(summary = "微信Token验证", description = "用于微信公众号后台验证服务器配置的专用接口")
    public void verifyWechatToken(
            @Parameter(description = "微信加密签名") @RequestParam String signature,
            @Parameter(description = "随机字符串") @RequestParam String echostr,
            @Parameter(description = "时间戳") @RequestParam String timestamp,
            @Parameter(description = "随机数") @RequestParam String nonce,
            HttpServletResponse response) throws IOException {
        
        log.info("收到微信Token验证请求(/user/wechat/verify) - signature: {}, timestamp: {}, nonce: {}, echostr: {}", 
                signature, timestamp, nonce, echostr);
        
        // 验证签名
        if (checkSignature(signature, timestamp, nonce)) {
            log.info("Token验证成功，返回echostr: {}", echostr);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().print(echostr);
        } else {
            log.error("Token验证失败 - 签名不匹配");
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().print("fail");
        }
    }

    /**
     * 微信服务器验证Token接口 - 兼容路径
     * 保留原有路径以兼容其他配置
     */
    @GetMapping("/token/verify")
    @Operation(summary = "微信Token验证(兼容)", description = "微信Token验证的兼容路径")
    public void verifyToken(
            @Parameter(description = "微信加密签名") @RequestParam String signature,
            @Parameter(description = "随机字符串") @RequestParam String echostr,
            @Parameter(description = "时间戳") @RequestParam String timestamp,
            @Parameter(description = "随机数") @RequestParam String nonce,
            HttpServletResponse response) throws IOException {
        
        log.info("收到微信Token验证请求(兼容路径) - signature: {}, timestamp: {}, nonce: {}, echostr: {}", 
                signature, timestamp, nonce, echostr);
        
        // 验证签名
        if (checkSignature(signature, timestamp, nonce)) {
            log.info("Token验证成功，返回echostr: {}", echostr);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().print(echostr);
        } else {
            log.error("Token验证失败");
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().print("fail");
        }
    }



    /**
     * 处理微信公众号事件推送 - 专用推送路径
     * 这个接口专门接收微信服务器推送的各种事件（关注、扫码等）
     * 通过nginx反向代理将根路径的POST请求转发到此路径
     */
    @PostMapping("/verify")
    @Operation(summary = "微信事件推送处理", description = "处理微信公众号的事件推送专用接口")
    public String handleWechatEvent(HttpServletRequest request) {
        try {
            // 解析微信推送的XML消息
            WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(request.getInputStream());
            
            log.info("收到微信事件推送(/user/wechat/verify) - 用户: {}, 消息类型: {}, 事件类型: {}", 
                    wxMessage.getFromUser(), wxMessage.getMsgType(), wxMessage.getEvent());
            
            // 将事件分发到处理器
            wechatEventHandlerService.dispatchEvent(wxMessage);
            
            return "success";
        } catch (Exception e) {
            log.error("处理微信事件推送失败(/user/wechat/verify)", e);
            return "fail";
        }
    }

    /**
     * 处理微信公众号事件推送 - 兼容路径
     * 保留原有路径以兼容其他配置
     */
    @PostMapping("/token/verify")
    @Operation(summary = "微信事件推送处理(兼容)", description = "微信事件推送处理的兼容路径")
    public String handleWxEvent(HttpServletRequest request) {
        try {
            // 解析微信推送的XML消息
            WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(request.getInputStream());
            
            log.info("收到微信事件推送(兼容路径) - 用户: {}, 消息类型: {}, 事件类型: {}", 
                    wxMessage.getFromUser(), wxMessage.getMsgType(), wxMessage.getEvent());
            
            // 将事件分发到处理器
            wechatEventHandlerService.dispatchEvent(wxMessage);
            
            return "success";
        } catch (Exception e) {
            log.error("处理微信事件推送失败(兼容路径)", e);
            return "fail";
        }
    }

    /**
     * 获取登录二维码
     * 生成临时二维码用于扫码登录，支持推广码绑定
     */
    @PostMapping("/qr/login")
    @Operation(summary = "获取登录二维码", description = "生成微信扫码登录的二维码，支持推广码绑定")
    public Result<Object> getLoginQrCode(@RequestParam(required = false) String inviteCode) {
        try {
            // 验证推广码（如果提供了）
            Long inviterId = null;
            if (StringUtils.hasText(inviteCode)) {
                User inviter = userService.getUserByInviteCode(inviteCode);
                if (inviter == null) {
                    return Result.error("推广码无效");
                }
                inviterId = inviter.getId();
                log.info("二维码生成时检测到推广码 - inviteCode: {}, inviterId: {}", inviteCode, inviterId);
            }

            // 生成场景值（用于标识这次登录请求）
            String sceneStr = System.currentTimeMillis() + "_" + (int)(Math.random() * 1000);
            
            // 创建临时二维码（有效期10分钟）
            WxMpQrCodeTicket qrCodeTicket = wxMpService.getQrcodeService()
                    .qrCodeCreateTmpTicket(sceneStr, 600);
            
            // 获取二维码图片URL
            String qrCodeUrl = wxMpService.getQrcodeService()
                    .qrCodePictureUrl(qrCodeTicket.getTicket());
            
            // 如果有推广码，存储到Redis
            if (inviterId != null) {
                String inviteKey = WX_LOGIN_SCENE_PREFIX + sceneStr + ":invite";
                redisTemplate.opsForValue().set(inviteKey, inviterId.toString(), 10, TimeUnit.MINUTES);
                log.info("推广码信息已存储到Redis - sceneStr: {}, inviterId: {}", sceneStr, inviterId);
            }
            
            log.info("生成登录二维码成功 - 场景值: {}, 二维码URL: {}, 推广码: {}", 
                    sceneStr, qrCodeUrl, inviteCode);
            
            // 返回二维码URL和场景值
            Map<String, String> data = new HashMap<>();
            data.put("qrCodeUrl", qrCodeUrl);
            data.put("sceneStr", sceneStr);
            if (StringUtils.hasText(inviteCode)) {
                data.put("inviteCode", inviteCode); // 返回推广码以便前端确认
            }
            
            return Result.success(data);
            
        } catch (WxErrorException e) {
            log.error("生成登录二维码失败", e);
            return Result.error("生成二维码失败：" + e.getMessage());
        }
    }

    /**
     * 轮询检查登录状态
     * 前端通过这个接口轮询检查用户是否已扫码登录
     */
    @PostMapping("/login/check")
    @Operation(summary = "检查登录状态", description = "轮询检查用户是否已扫码登录")
    public Result<AuthDto.LoginResponse> checkLoginStatus(@RequestParam String sceneStr) {
        try {
            String setKey = WX_LOGIN_SCENE_PREFIX + sceneStr;
            
            // 从Redis Set中获取所有openid
            Set<String> openids = redisTemplate.opsForSet().members(setKey);
            
            if (openids != null && !openids.isEmpty()) {
                // 取第一个openid进行登录（先到先得）
                String openid = openids.iterator().next();
                log.info("用户扫码登录成功 - 场景值: {}, openid: {}, 总扫码用户数: {}", sceneStr, openid, openids.size());
                
                // 从Set中移除已使用的openid
                redisTemplate.opsForSet().remove(setKey, openid);
                
                // 如果Set为空，删除整个key
                Long remainingCount = redisTemplate.opsForSet().size(setKey);
                if (remainingCount == null || remainingCount == 0) {
                    redisTemplate.delete(setKey);
                }
                
                // 执行微信登录逻辑（传入场景值以支持推广码）
                AuthDto.LoginResponse loginResponse = userService.wechatLogin(openid, sceneStr);
                return Result.success(loginResponse);
            } else {
                return Result.error("用户尚未扫码");
            }
            
        } catch (Exception e) {
            log.error("检查登录状态失败", e);
            return Result.error("检查登录状态失败");
        }
    }

    /**
     * 验证微信签名
     * 
     * @param signature 微信传递的签名
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @return 是否验证通过
     */
    private boolean checkSignature(String signature, String timestamp, String nonce) {
        try {
            // 1. 将token、timestamp、nonce三个参数进行字典序排序
            String[] arr = new String[]{token, timestamp, nonce};
            Arrays.sort(arr);
            
            // 2. 将三个参数字符串拼接成一个字符串
            StringBuilder sb = new StringBuilder();
            for (String s : arr) {
                sb.append(s);
            }
            String str = sb.toString();
            
            // 3. 对拼接后的字符串进行SHA1加密
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(str.getBytes(StandardCharsets.UTF_8));
            
            // 4. 将加密后的字符串与signature进行比较
            StringBuilder hexStr = new StringBuilder();
            for (byte b : digest) {
                String hex = Integer.toHexString(b & 0xFF);
                if (hex.length() == 1) {
                    hexStr.append('0');
                }
                hexStr.append(hex);
            }
            
            return hexStr.toString().equals(signature);
            
        } catch (NoSuchAlgorithmException e) {
            log.error("SHA1加密失败", e);
            return false;
        }
    }

    /**
     * 增强的微信消息处理方法
     * 
     * @param wxMessage 微信消息对象
     */
    private void handleQrCodeEvent(WxMpXmlMessage wxMessage) {
        String msgType = wxMessage.getMsgType();
        String event = wxMessage.getEvent();
        String eventKey = wxMessage.getEventKey();
        String fromUser = wxMessage.getFromUser();
        
        log.info("处理微信消息 - 消息类型: {}, 事件类型: {}, 事件Key: {}, 用户: {}", msgType, event, eventKey, fromUser);
        
        try {
            if ("event".equals(msgType)) {
                // 处理事件消息
                switch (event) {
                    case "subscribe":
                        // 使用新的事件处理服务
                        wechatEventHandlerService.handleSubscribeEvent(wxMessage);
                        // 保持原有的登录逻辑
                        if (eventKey != null && eventKey.startsWith("qrscene_")) {
                            String sceneStr = eventKey.replace("qrscene_", "");
                            handleSceneEvent(sceneStr, fromUser);
                        }
                        break;
                    case "unsubscribe":
                        wechatEventHandlerService.handleUnsubscribeEvent(wxMessage);
                        break;
                    case "SCAN":
                        // 使用新的事件处理服务
                        wechatEventHandlerService.handleScanEvent(wxMessage);
                        // 保持原有的扫码逻辑
                        handleSceneEvent(eventKey, fromUser);
                        break;
                    case "CLICK":
                        wechatEventHandlerService.handleMenuClickEvent(wxMessage);
                        break;
                    case "VIEW":
                        wechatEventHandlerService.handleMenuViewEvent(wxMessage);
                        break;
                    default:
                        log.warn("未处理的事件类型: {}", event);
                }
            } else if ("text".equals(msgType)) {
                // 处理文本消息（关键词回复）
                wechatEventHandlerService.handleTextMessage(wxMessage);
            } else {
                // 处理其他类型消息
                wechatEventHandlerService.handleOtherMessage(wxMessage);
            }
        } catch (Exception e) {
            log.error("处理微信消息失败 - msgType: {}, event: {}, fromUser: {}", msgType, event, fromUser, e);
        }
    }

    /**
     * 处理场景事件（登录或绑定）
     * 
     * @param sceneStr 场景值
     * @param openid 用户openid
     */
    private void handleSceneEvent(String sceneStr, String openid) {
        try {
            log.info("处理场景事件 - 场景值: {}, openid: {}", sceneStr, openid);
            
            // 判断是登录场景还是绑定场景
            if (sceneStr.startsWith("bind_")) {
                // 绑定场景
                log.info("识别为绑定场景");
                handleBindEvent(sceneStr, openid);
            } else {
                // 登录场景
                log.info("识别为登录场景");
                saveLoginInfo(sceneStr, openid);
            }
        } catch (Exception e) {
            log.error("处理场景事件失败 - 场景值: {}, openid: {}", sceneStr, openid, e);
        }
    }

    /**
     * 处理微信绑定事件
     * 
     * @param sceneStr 场景值
     * @param openid 用户openid
     */
    private void handleBindEvent(String sceneStr, String openid) {
        try {
            log.info("处理微信绑定事件 - 场景值: {}, openid: {}", sceneStr, openid);
            
            // 从Redis获取绑定信息（场景值 -> 用户ID）
            String userIdStr = redisTemplate.opsForValue().get(WX_BIND_SCENE_PREFIX + sceneStr);
            if (!StringUtils.hasText(userIdStr)) {
                log.error("微信绑定失败：二维码已过期或无效 - 场景值: {}", sceneStr);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "failed", 10, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":error", "二维码已过期或无效", 10, TimeUnit.MINUTES);
                return;
            }
            
            Long userId = Long.valueOf(userIdStr);
            
            // 检查当前用户是否存在
            User user = userService.getUserById(userId);
            if (user == null) {
                log.error("微信绑定失败：用户不存在 - 用户ID: {}", userId);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "failed", 10, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":error", "用户不存在", 10, TimeUnit.MINUTES);
                return;
            }
            
            // 检查用户是否已绑定微信
            if (StringUtils.hasText(user.getWechatOpenid())) {
                log.error("微信绑定失败：用户已绑定其他微信账号 - 用户ID: {}", userId);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "failed", 10, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":error", "用户已绑定其他微信账号", 10, TimeUnit.MINUTES);
                return;
            }
            
            // 检查该微信账号是否已绑定其他用户
            User existingUser = userService.getUserByWechatOpenid(openid);
            if (existingUser != null) {
                log.error("微信绑定失败：该微信账号已绑定其他用户 - openid: {}", openid);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "failed", 10, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":error", "该微信账号已绑定其他用户", 10, TimeUnit.MINUTES);
                return;
            }
            
            // 执行绑定操作
            boolean success = userService.bindWechatAccount(userId, openid);
            
            if (success) {
                // 绑定成功，更新用户信息
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "success", 10, TimeUnit.MINUTES);
                log.info("微信绑定成功 - 用户ID: {}, openid: {}", userId, openid);
            } else {
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "failed", 10, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":error", "绑定失败，请重试", 10, TimeUnit.MINUTES);
                log.error("微信绑定失败：数据库更新失败 - 用户ID: {}", userId);
            }
            
        } catch (Exception e) {
            log.error("处理微信绑定事件失败 - 场景值: {}, openid: {}", sceneStr, openid, e);
            redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":completed", "failed", 10, TimeUnit.MINUTES);
            redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr + ":error", "系统错误，请重试", 10, TimeUnit.MINUTES);
        }
    }

    /**
     * 保存登录信息到Redis（支持多用户扫同一二维码）
     * 
     * @param sceneStr 场景值
     * @param openid 用户openid
     */
    private void saveLoginInfo(String sceneStr, String openid) {
        try {
            // 使用Set结构存储多个openid，避免覆盖问题
            String setKey = WX_LOGIN_SCENE_PREFIX + sceneStr;
            redisTemplate.opsForSet().add(setKey, openid);
            redisTemplate.expire(setKey, 10, TimeUnit.MINUTES);
            log.info("用户扫码成功 - 场景值: {}, openid: {}", sceneStr, openid);
        } catch (Exception e) {
            log.error("保存登录信息失败", e);
        }
    }

    /**
     * 获取微信绑定二维码
     * 生成临时二维码用于绑定已有账号到微信
     */
    @PostMapping("/qr/bind")
    @Operation(summary = "获取微信绑定二维码", description = "生成微信绑定已有账号的二维码")
    public Result<Object> getBindQrCode(HttpServletRequest request) {
        try {
            // 从JWT获取当前用户ID
            String authHeader = request.getHeader("Authorization");
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return Result.error(401, "未提供认证令牌");
            }

            String token = authHeader.substring(7);
            if (!jwtUtil.validateToken(token)) {
                return Result.error(401, "认证令牌无效或已过期");
            }

            Long userId = jwtUtil.getUserIdFromToken(token);
            
            // 检查用户是否已绑定微信
            User user = userService.getUserById(userId);
            if (user == null) {
                return Result.error(400, "用户不存在");
            }
            
            if (StringUtils.hasText(user.getWechatOpenid())) {
                return Result.error(400, "用户已绑定微信账号");
            }

            // 生成场景值（用于标识这次绑定请求）
            String sceneStr = "bind_" + userId + "_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000);
            
            // 创建临时二维码（有效期10分钟）
            WxMpQrCodeTicket qrCodeTicket = wxMpService.getQrcodeService()
                    .qrCodeCreateTmpTicket(sceneStr, 600);
            
            // 获取二维码图片URL
            String qrCodeUrl = wxMpService.getQrcodeService()
                    .qrCodePictureUrl(qrCodeTicket.getTicket());
            
            // 在Redis中存储绑定信息（场景值 -> 用户ID）
            redisTemplate.opsForValue().set(WX_BIND_SCENE_PREFIX + sceneStr, String.valueOf(userId), 10, TimeUnit.MINUTES);
            
            log.info("生成微信绑定二维码成功 - 用户ID: {}, 场景值: {}, 二维码URL: {}", userId, sceneStr, qrCodeUrl);
            
            // 返回二维码URL和场景值
            Map<String, String> data = new HashMap<>();
            data.put("qrCodeUrl", qrCodeUrl);
            data.put("sceneStr", sceneStr);
            
            return Result.success(data);
            
        } catch (WxErrorException e) {
            log.error("生成微信绑定二维码失败", e);
            return Result.error(500, "生成绑定二维码失败：" + e.getMessage());
        } catch (Exception e) {
            log.error("生成微信绑定二维码失败", e);
            return Result.error(500, "生成绑定二维码失败：" + e.getMessage());
        }
    }

    /**
     * 轮询检查绑定状态
     * 前端通过这个接口轮询检查用户是否已扫码完成绑定
     */
    @PostMapping("/bind/check")
    @Operation(summary = "检查微信绑定状态", description = "轮询检查用户是否已完成微信绑定")
    public Result<String> checkBindStatus(@RequestParam String sceneStr, HttpServletRequest request) {
        try {
            // 验证JWT令牌
            String authHeader = request.getHeader("Authorization");
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return Result.error(401, "未提供认证令牌");
            }

            String token = authHeader.substring(7);
            if (!jwtUtil.validateToken(token)) {
                return Result.error(401, "认证令牌无效或已过期");
            }

            Long userId = jwtUtil.getUserIdFromToken(token);
            
            // 检查Redis中是否有绑定完成的标记
            String bindResult = redisTemplate.opsForValue().get(WX_BIND_SCENE_PREFIX + sceneStr + ":completed");
            
            if ("success".equals(bindResult)) {
                // 绑定成功，清除Redis中的临时数据
                redisTemplate.delete(WX_BIND_SCENE_PREFIX + sceneStr);
                redisTemplate.delete(WX_BIND_SCENE_PREFIX + sceneStr + ":completed");
                
                log.info("微信绑定成功 - 用户ID: {}, 场景值: {}", userId, sceneStr);
                return Result.success("微信绑定成功");
            } else if ("failed".equals(bindResult)) {
                // 绑定失败
                String errorMsg = redisTemplate.opsForValue().get(WX_BIND_SCENE_PREFIX + sceneStr + ":error");
                redisTemplate.delete(WX_BIND_SCENE_PREFIX + sceneStr);
                redisTemplate.delete(WX_BIND_SCENE_PREFIX + sceneStr + ":completed");
                redisTemplate.delete(WX_BIND_SCENE_PREFIX + sceneStr + ":error");
                
                log.error("微信绑定失败 - 用户ID: {}, 场景值: {}, 错误: {}", userId, sceneStr, errorMsg);
                return Result.error(400, errorMsg != null ? errorMsg : "微信绑定失败");
            } else {
                // 还未扫码或正在处理中
                return Result.error(202, "用户尚未扫码或正在处理中");
            }
            
        } catch (Exception e) {
            log.error("检查微信绑定状态失败", e);
            return Result.error(500, "检查绑定状态失败");
        }
    }
}