package com.xindazhou.message.event;

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.xindazhou.framework.security.core.jwt.OAuth2JwtUtil;
import com.xindazhou.vehicle.api.VehicleControlApi;
import com.xindazhou.vehicle.api.dto.control.VehicleControlReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.util.Map;

/**
 * 车辆控制事件处理器
 *
 * @author xindazhou
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class VehicleControlEventHandler {

    private final SocketIOServer socketIOServer;
    
    @Resource
    private VehicleControlApi vehicleControlApi;

    /**
     * 客户端连接事件
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        String sessionId = client.getSessionId().toString();
        log.info("客户端连接: sessionId={}, remoteAddress={}", sessionId, client.getRemoteAddress());
        
        // 1. OAuth2 JWT Token 校验
        String token = getTokenFromClient(client);
        log.info("从客户端获取 Token: sessionId={}, tokenLength={}, tokenPrefix={}", 
                sessionId, token != null ? token.length() : 0, 
                token != null && token.length() > 20 ? token.substring(0, 20) + "..." : token);
        
        if (StrUtil.isEmpty(token)) {
            log.warn("客户端连接失败: 未提供 Token, sessionId={}", sessionId);
            log.debug("Handshake Headers: {}", client.getHandshakeData().getHttpHeaders());
            log.debug("Handshake URL Params: {}", client.getHandshakeData().getUrlParams());
            client.disconnect();
            return;
        }
        
        // 2. 本地验证 JWT（验证签名 + 过期时间）
        DecodedJWT jwt = null;
        try {
            jwt = OAuth2JwtUtil.verifyToken(token);
            if (jwt == null) {
                log.warn("客户端连接失败: OAuth2 JWT Token 验证失败（返回 null）, sessionId={}, tokenLength={}", 
                        sessionId, token.length());
                // 注意：OAuth2JwtUtil.verifyToken 内部已经记录了详细的验证失败原因
                client.disconnect();
                return;
            }
            log.info("Token 验证成功: sessionId={}, userId={}", sessionId, OAuth2JwtUtil.getUserId(jwt));
        } catch (Exception e) {
            log.error("客户端连接失败: OAuth2 JWT Token 验证异常, sessionId={}, error={}", sessionId, e.getMessage(), e);
            client.disconnect();
            return;
        }
        
        // 3. 从 JWT 中提取用户信息
        Long userId = OAuth2JwtUtil.getUserId(jwt);
        Integer userType = OAuth2JwtUtil.getUserType(jwt);
        
        if (userId == null) {
            log.warn("客户端连接失败: 无法从 Token 中获取用户ID, sessionId={}", sessionId);
            client.disconnect();
            return;
        }
        
        // 3. 存储用户信息到客户端
        client.set("userId", userId);
        client.set("userType", userType);
        
        // 4. 加入用户房间
        String userRoom = "user:" + userId;
        client.joinRoom(userRoom);
        log.info("客户端连接成功: sessionId={}, userId={}, userType={}, room={}", 
                sessionId, userId, userType, userRoom);
        
        // TODO: 5. 更新在线用户表（Redis）
    }
    
    /**
     * 从客户端获取 Token
     * SocketIO 连接时，Token 可以通过以下方式传递：
     * 1. URL 参数：?token=xxx
     * 2. Handshake 数据
     */
    private String getTokenFromClient(SocketIOClient client) {
        // 方式1：从 Handshake 数据中获取（HTTP Headers）
        String authHeader = client.getHandshakeData().getHttpHeaders().get("Authorization");
        if (StrUtil.isNotEmpty(authHeader) && authHeader.startsWith("Bearer ")) {
            return authHeader.substring(7);
        }
        
        // 方式2：从 URL 参数中获取（URL Params 返回的是 List<String>）
        Map<String, java.util.List<String>> urlParams = client.getHandshakeData().getUrlParams();
        java.util.List<String> tokenList = urlParams.get("token");
        if (tokenList != null && !tokenList.isEmpty()) {
            String token = tokenList.get(0);
            if (StrUtil.isNotEmpty(token)) {
                return token;
            }
        }
        
        // 方式3：从 Single 参数中获取
        String token = client.getHandshakeData().getSingleUrlParam("token");
        return token;
    }

    /**
     * 客户端断开连接事件
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String sessionId = client.getSessionId().toString();
        Long userId = client.get("userId");
        log.info("客户端断开连接: sessionId={}, userId={}", sessionId, userId);
        
        // 1. 从房间移除（SocketIO 会自动处理）
        // 2. 更新在线用户表（Redis）
        if (userId != null) {
            // TODO: 从 Redis 中移除在线用户记录
        }
    }

    /**
     * 车辆控制指令事件
     */
    @OnEvent("vehicle_control")
    public void onVehicleControl(SocketIOClient client, Map<String, Object> data, AckRequest ackRequest) {
        String sessionId = client.getSessionId().toString();
        Long userId = client.get("userId");
        log.info("收到车辆控制指令: sessionId={}, userId={}, data={}", sessionId, userId, data);
        
        try {
            // 1. 权限校验
            if (userId == null) {
                log.warn("车辆控制指令失败: 用户未登录, sessionId={}", sessionId);
                if (ackRequest.isAckRequested()) {
                    ackRequest.sendAckData(Map.of("success", false, "message", "用户未登录"));
                }
                return;
            }
            
            // 2. 获取指令信息
            String command = (String) data.get("command");
            if (StrUtil.isEmpty(command)) {
                log.warn("车辆控制指令失败: 指令类型为空, sessionId={}", sessionId);
                if (ackRequest.isAckRequested()) {
                    ackRequest.sendAckData(Map.of("success", false, "message", "指令类型不能为空"));
                }
                return;
            }
            
            // 3. 调用车辆服务处理指令
            log.info("执行车辆控制指令: userId={}, command={}, data={}", userId, command, data);
            
            // 构建车辆控制请求
            Long vehicleId = null;
            if (data.get("vehicleId") != null) {
                vehicleId = ((Number) data.get("vehicleId")).longValue();
            }
            Integer userType = client.get("userType");
            
            VehicleControlReqDTO reqDTO = new VehicleControlReqDTO();
            reqDTO.setVehicleId(vehicleId != null ? vehicleId : 1L); // 默认车辆ID为1
            reqDTO.setUserId(userId);
            reqDTO.setUserType(userType != null ? userType : 2); // 默认用户类型为2（C端用户）
            reqDTO.setCommand(command);
            reqDTO.setParams(data);
            
            // 调用车辆服务
            boolean controlSuccess = false;
            String errorMessage = null;
            try {
                var controlResult = vehicleControlApi.controlVehicle(reqDTO);
                if (controlResult != null && controlResult.getData() != null && controlResult.getData()) {
                    controlSuccess = true;
                    log.info("车辆控制指令执行成功: userId={}, command={}, vehicleId={}", userId, command, reqDTO.getVehicleId());
                } else {
                    errorMessage = controlResult != null && controlResult.getMsg() != null ? controlResult.getMsg() : "车辆服务返回失败";
                    log.warn("车辆控制指令执行失败: userId={}, command={}, error={}", userId, command, errorMessage);
                }
            } catch (Exception e) {
                errorMessage = "调用车辆服务异常: " + e.getMessage();
                log.error("调用车辆服务异常: userId={}, command={}", userId, command, e);
            }
            
            // 4. 返回结果
            Map<String, Object> result = new java.util.HashMap<>();
            result.put("success", controlSuccess);
            result.put("message", controlSuccess ? "指令已接收并处理" : (errorMessage != null ? errorMessage : "指令处理失败"));
            result.put("command", command);
            result.put("vehicleId", reqDTO.getVehicleId());
            result.put("timestamp", System.currentTimeMillis());
            
            // 发送ACK响应
            if (ackRequest.isAckRequested()) {
                ackRequest.sendAckData(result);
            }
            
            // 发送事件通知（可选）
            String userRoom = "user:" + userId;
            socketIOServer.getRoomOperations(userRoom).sendEvent("vehicle_control_response", result);
            
        } catch (Exception e) {
            log.error("车辆控制指令处理异常: sessionId={}, userId={}", sessionId, userId, e);
            if (ackRequest.isAckRequested()) {
                ackRequest.sendAckData(Map.of("success", false, "message", "指令处理失败: " + e.getMessage()));
            }
        }
    }

    /**
     * 心跳事件
     */
    @OnEvent("ping")
    public void onPing(SocketIOClient client, AckRequest ackRequest) {
        if (ackRequest.isAckRequested()) {
            ackRequest.sendAckData("pong");
        }
    }

}

