package shop.alien.store.config;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import shop.alien.entity.store.LifeBlacklist;
import shop.alien.entity.store.LifeMessage;
import shop.alien.entity.store.LifeUser;
import shop.alien.entity.store.StoreTrackEvent;
import shop.alien.entity.store.StoreUser;
import shop.alien.entity.store.vo.WebSocketVo;
import shop.alien.mapper.LifeBlacklistMapper;
import shop.alien.mapper.LifeMessageMapper;
import shop.alien.mapper.LifeUserMapper;
import shop.alien.mapper.StoreUserMapper;
import shop.alien.store.config.BaseRedisService;
import shop.alien.store.util.UserAgentParserUtil;
import shop.alien.util.common.safe.ImageReviewServiceEnum;
import shop.alien.util.common.safe.TextModerationResultVO;
import shop.alien.util.common.safe.TextModerationUtil;
import shop.alien.util.common.safe.TextReviewServiceEnum;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author ssk
 * @version 1.0
 * @date 2024/2/29 14:42
 */
@Slf4j
@Component
@ServerEndpoint(value = "/socket/{sendId}", configurator = WebSocketConfig.WebSocketConfigurator.class)
public class WebSocketProcess implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    private static LifeBlacklistMapper lifeBlacklistMapper;
    private static LifeMessageMapper lifeMessageMapper;
    private static BaseRedisService baseRedisService;
    private static TextModerationUtil textModerationUtil;
    private static StoreUserMapper storeUserMapper;
    private static LifeUserMapper lifeUserMapper;
    private static ObjectMapper objectMapper;
    /*
     * 持有每个webSocket对象，以key-value存储到线程安全ConcurrentHashMap，
     */
    private static final ConcurrentHashMap<String, WebSocketProcess> concurrentHashMap = new ConcurrentHashMap<>(12);
    
    /**
     * 存储每个连接的IP地址和User-Agent信息
     * key: sendId, value: Map包含ipAddress和userAgent
     */
    private static final ConcurrentHashMap<String, Map<String, String>> connectionInfoMap = new ConcurrentHashMap<>(12);

    @Override
    public void setApplicationContext(ApplicationContext context) {
        WebSocketProcess.applicationContext = context;
        WebSocketProcess.lifeBlacklistMapper = context.getBean(LifeBlacklistMapper.class);
        WebSocketProcess.lifeMessageMapper = context.getBean(LifeMessageMapper.class);
        WebSocketProcess.baseRedisService = context.getBean(BaseRedisService.class);
        WebSocketProcess.textModerationUtil = context.getBean(TextModerationUtil.class);
        WebSocketProcess.storeUserMapper = context.getBean(StoreUserMapper.class);
        WebSocketProcess.lifeUserMapper = context.getBean(LifeUserMapper.class);
        WebSocketProcess.objectMapper = context.getBean(ObjectMapper.class);
    }

    /**
     * 会话对象
     **/
    private Session session;
    
    /**
     * 当前连接的IP地址
     */
    private String ipAddress;
    
    /**
     * 当前连接的User-Agent
     */
    private String userAgent;

    /**
     * 接收到客户端消息时触发
     */
    @OnMessage
    public void onMessage(@PathParam("sendId") String id, String message) throws Exception {
        try {
            // 解析消息
            WebSocketVo webSocketVo = JSONObject.parseObject(message, WebSocketVo.class);

            // 过滤心跳
            if (null == webSocketVo || "heartbeat".equals(webSocketVo.getCategory())) return;

            // 记录已读消息id
            if ("receipt".equals(webSocketVo.getCategory())) {
                baseRedisService.setListRight("readMessageIdKey", String.valueOf(webSocketVo.getMessageId()));
                return;
            }

            // 检查消息合规性
//            if (!checkCompliance(webSocketVo)) {
//                webSocketVo.setType("7");
//                webSocketVo.setText("发送内容存在违规行为");
//                sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
//                return;
//            }

            // 发送位置信息
            if ("position".equals(webSocketVo.getCategory())) {
                sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
                sendMessage(webSocketVo.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
                return;
            }

            log.info("webSocketVo----------------{}", JSONObject.from(webSocketVo).toJSONString());
            log.info("concurrentHashMap----------{}", concurrentHashMap.keySet());

            // 保存消息记录
            LifeMessage lifeMessage = new LifeMessage();
            lifeMessage.setSenderId(webSocketVo.getSenderId());
            lifeMessage.setReceiverId(webSocketVo.getReceiverId());
            lifeMessage.setContent(webSocketVo.getText());
            lifeMessage.setType(webSocketVo.getType());
            lifeMessage.setBusinessId(webSocketVo.getBusinessId());
            // 查询自己是否在对方的黑名单中
            if (baseRedisService.hasKey("blackList_" + webSocketVo.getSenderId())) {
                List<String> blackList = baseRedisService.getList("blackList_" + webSocketVo.getSenderId());
                if (blackList.contains(webSocketVo.getReceiverId())) {
                    lifeMessage.setDeletePhoneId(webSocketVo.getReceiverId());
                    lifeMessageMapper.insert(lifeMessage);
                    // 发送消息
                    webSocketVo.setMessageId(lifeMessage.getId());
                    webSocketVo.setCategory("message");
                    webSocketVo.setCreatedTime(lifeMessage.getCreatedTime());
                    sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
                    return;
                }
            }

            lifeMessageMapper.insert(lifeMessage);
            
            // 记录咨询埋点：如果接收者是店铺（receiverId以store_开头），且是普通消息（不是heartbeat、receipt、position、notice）
            String category = webSocketVo.getCategory();
            if (webSocketVo.getReceiverId() != null && webSocketVo.getReceiverId().startsWith("store_") 
                    && category != null && !"heartbeat".equals(category) && !"receipt".equals(category) 
                    && !"position".equals(category) && !"notice".equals(category)) {
                recordConsultEvent(webSocketVo, id);
            }
            
            // 记录店铺分享埋点：消息类型是分享（type="3"链接分享 或 type="14"人员分享）
            String messageType = webSocketVo.getType();
            if ("3".equals(messageType) || "14".equals(messageType)) {
                recordShareEvent(webSocketVo, id);
            }
            
            // 发送消息
            webSocketVo.setMessageId(lifeMessage.getId());
            webSocketVo.setCategory("message");
            webSocketVo.setCreatedTime(lifeMessage.getCreatedTime());
            sendMessage(webSocketVo.getSenderId(), JSONObject.from(webSocketVo).toJSONString());
            sendMessage(webSocketVo.getReceiverId(), JSONObject.from(webSocketVo).toJSONString());
        } catch (Exception e) {
            log.error("WebSocketProcess.onMessage()----Exception----Message={}", e.getMessage());
        }
    }

    /**
     * 发送消息到指定客户端
     */
    public void sendMessage(String id, String message) throws Exception {
        try {
            log.info("WebSocketProcess.sendMessage()--readySend----id={},message={}", id, message);
            // 根据id,从map中获取存储的webSocket对象
            WebSocketProcess webSocketProcess = concurrentHashMap.get(id);
            if (!ObjectUtils.isEmpty(webSocketProcess)) {
                // 当客户端是Open状态时，才能发送消息
                if (webSocketProcess.session.isOpen()) {
                    webSocketProcess.session.getBasicRemote().sendText(message);
                    log.info("WebSocketProcess.sendMessage()---sendSuccess---id={},message={}", id, message);
                } else {
                    log.error("WebSocketProcess.sendMessage()---sendError----websocket session:{} is closed ", id);
                }
            } else {
                log.error("WebSocketProcess.sendMessage()---sendError----websocket session:{} is not exit ", id);
            }
        } catch (Exception e) {
            log.error("WebSocketProcess.sendMessage()---Exception----Message={}", e.getMessage());
        }
    }

    /**
     * 客户端创建连接时触发
     * */
    @OnOpen
    public void onOpen(@PathParam("sendId") String id, Session session) {
        try {
            //每新建立一个连接，就把当前客户id为key,this为value存储到map中
            this.session = session;
            // 设置 5 分钟没有任何消息往来则自动关闭连接，防止长连接堆积
            this.session.setMaxIdleTimeout(5 * 60 * 1000);
            concurrentHashMap.put(id, this);
            
            // 从Session的UserProperties中获取IP地址和User-Agent（由WebSocketConfigurator在握手时设置）
            Map<String, Object> userProperties = session.getUserProperties();
            this.ipAddress = (String) userProperties.get("ipAddress");
            this.userAgent = (String) userProperties.get("userAgent");
            
            // 保存连接信息到Map中
            Map<String, String> connectionInfo = new HashMap<>();
            connectionInfo.put("ipAddress", this.ipAddress);
            connectionInfo.put("userAgent", this.userAgent);
            connectionInfoMap.put(id, connectionInfo);
            
            log.info("WebSocketProcess.onOpen() Open a websocket. id={}, ipAddress={}, userAgent={}", id, this.ipAddress, this.userAgent);

            // 获取拉黑自己的用户信息
            LambdaQueryWrapper<LifeBlacklist> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(LifeBlacklist::getBlockedPhoneId, id);
            List<String> blackList = lifeBlacklistMapper.selectList(wrapper).stream().map(LifeBlacklist::getBlockerPhoneId).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(blackList)) baseRedisService.setSaveOrOverwriteScriptList("blackList_" + id, blackList);
        } catch (Exception e) {
            log.error("WebSocketProcess.onOpen()----Exception----Message={}", e.getMessage());
        }
    }


    /**
     * 客户端连接关闭时触发
     **/
    @OnClose
    public void onClose(Session session, @PathParam("sendId") String id) {
        try {
            //客户端连接关闭时，安全移除map中存储的键值对
            boolean removed = concurrentHashMap.remove(id, this);
            if (removed) {
                log.info("WebSocketProcess.onClose() close a websocket, concurrentHashMap remove sessionId= {}", id);
                if (baseRedisService.hasKey("blackList_" + id)) baseRedisService.delete("blackList_" + id);
                connectionInfoMap.remove(id);
            }
        } catch (Exception e) {
            log.error("WebSocketProcess.onClose()----Exception----Message={}", e.getMessage());
        }
    }

    /**
     * 连接发生异常时候触发
     */
    @OnError
    public void onError(@PathParam("sendId") String id, Throwable error) {
        try {
            log.error("WebSocketProcess.onError() Error,id={}, Msg=", id, error);
            // 发生错误时主动移除，防止僵尸连接
            concurrentHashMap.remove(id, this);
            connectionInfoMap.remove(id);
        } catch (Exception e) {
            log.error("WebSocketProcess.onError()----Exception----Message={}", e.getMessage());
        }
    }

    /**
     * 验证消息合规性
     */
    private boolean checkCompliance(WebSocketVo websocketVo) throws Exception {
        try {
            List<String> servicesList = Lists.newArrayList();
            TextModerationResultVO textModerationResultVO = null;
            if ("1".equals(websocketVo.getType())) {
                servicesList.add(TextReviewServiceEnum.AD_COMPLIANCE_DETECTION_PRO.getService());
                servicesList.add(TextReviewServiceEnum.CHAT_DETECTION_PRO.getService());
                textModerationResultVO = textModerationUtil.invokeFunction(websocketVo.getText(), servicesList);

            } else if ("2".equals(websocketVo.getType())) {
                servicesList.add(ImageReviewServiceEnum.TONALITY_IMPROVE.getService());
                textModerationResultVO = textModerationUtil.invokeFunction(websocketVo.getText(), servicesList);
            }
            return !(null != textModerationResultVO && "high".equals(textModerationResultVO.getRiskLevel()));
        } catch (Exception e) {
            log.error("WebSocketProcess.checkCompliance()----Exception----Message={}", e.getMessage());
            return true;
        }
    }

    /**
     * 发送消息到所有客户端
     */
    public void sendAllMessage(String msg) throws Exception {
        Set<Map.Entry<String, WebSocketProcess>> entries = concurrentHashMap.entrySet();
        for (Map.Entry<String, WebSocketProcess> entry : entries) {
            String cid = entry.getKey();
            WebSocketProcess webSocketProcess = entry.getValue();
            boolean sessionOpen = webSocketProcess.session.isOpen();
            if (sessionOpen) {
                webSocketProcess.session.getBasicRemote().sendText(msg);
            } else {
                log.info("cid={} is closed,ignore send text", cid);
            }
        }
    }
    
    /**
     * 记录咨询埋点事件
     */
    private void recordConsultEvent(WebSocketVo webSocketVo, String sendId) {
        try {
            // 从receiverId（格式：store_手机号）获取店铺ID
            Integer storeId = null;
            if (webSocketVo.getReceiverId() != null && webSocketVo.getReceiverId().startsWith("store_")) {
                String phone = webSocketVo.getReceiverId().substring(6); // 去掉 "store_" 前缀
                LambdaQueryWrapper<StoreUser> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(StoreUser::getPhone, phone)
                        .eq(StoreUser::getDeleteFlag, 0);
                StoreUser storeUser = storeUserMapper.selectOne(wrapper);
                if (storeUser != null && storeUser.getStoreId() != null) {
                    storeId = storeUser.getStoreId();
                }
            }
            
            // 从senderId（格式：user_手机号）获取用户ID
            Integer userId = null;
            if (webSocketVo.getSenderId() != null && webSocketVo.getSenderId().startsWith("user_")) {
                String phone = webSocketVo.getSenderId().substring(5); // 去掉 "user_" 前缀
                LambdaQueryWrapper<LifeUser> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(LifeUser::getUserPhone, phone)
                        .eq(LifeUser::getDeleteFlag, 0);
                LifeUser lifeUser = lifeUserMapper.selectOne(wrapper);
                if (lifeUser != null && lifeUser.getId() != null) {
                    userId = lifeUser.getId();
                }
            }
            
            // 创建咨询埋点事件
            if (storeId != null) {
                StoreTrackEvent trackEvent = new StoreTrackEvent();
                trackEvent.setEventType("CONSULT");
                trackEvent.setEventCategory("INTERACTION");
                trackEvent.setStoreId(storeId);
                trackEvent.setUserId(userId);
                trackEvent.setTargetId(storeId);
                trackEvent.setTargetType("STORE");
                trackEvent.setEventTime(new java.util.Date());
                
                // 获取IP地址、User-Agent和设备类型
                Map<String, String> connectionInfo = connectionInfoMap.get(sendId);
                if (connectionInfo != null) {
                    trackEvent.setIpAddress(connectionInfo.get("ipAddress"));
                    trackEvent.setUserAgent(connectionInfo.get("userAgent"));
                    trackEvent.setDeviceType(UserAgentParserUtil.parseDeviceType(connectionInfo.get("userAgent")));
                } else {
                    // 如果Map中没有，尝试从当前实例获取
                    trackEvent.setIpAddress(this.ipAddress);
                    trackEvent.setUserAgent(this.userAgent);
                    trackEvent.setDeviceType(UserAgentParserUtil.parseDeviceType(this.userAgent));
                }
                
                // 异步写入Redis List
                String eventJson = objectMapper.writeValueAsString(trackEvent);
                baseRedisService.setListRight("track:event:queue", eventJson);
                
                log.debug("WebSocket咨询埋点记录成功: storeId={}, userId={}, ipAddress={}, userAgent={}", 
                        storeId, userId, trackEvent.getIpAddress(), trackEvent.getUserAgent());
            } else {
                log.warn("WebSocket咨询埋点记录失败: 无法获取storeId, receiverId={}", webSocketVo.getReceiverId());
            }
        } catch (Exception e) {
            log.error("WebSocket咨询埋点记录异常: receiverId={}, senderId={}", 
                    webSocketVo.getReceiverId(), webSocketVo.getSenderId(), e);
        }
    }
    
    /**
     * 记录店铺分享埋点事件
     */
    private void recordShareEvent(WebSocketVo webSocketVo, String sendId) {
        try {
            Integer storeId = null;
            Integer userId = null;
            
            // 从消息内容中解析storeId（消息格式：{"sendType":"shopShare","url":"{...storeId:256...}"}）
            try {
                String text = webSocketVo.getText();
                if (text != null) {
                    // 解析第一层JSON
                    JSONObject textJson = JSONObject.parseObject(text);
                    String sendType = textJson.getString("sendType");
                    
                    // 如果是店铺分享
                    if ("shopShare".equals(sendType)) {
                        String urlStr = textJson.getString("url");
                        if (urlStr != null) {
                            // 解析url字段（也是JSON字符串）
                            JSONObject urlJson = JSONObject.parseObject(urlStr);
                            Object storeIdObj = urlJson.get("storeId");
                            if (storeIdObj != null) {
                                if (storeIdObj instanceof Integer) {
                                    storeId = (Integer) storeIdObj;
                                } else if (storeIdObj instanceof Number) {
                                    storeId = ((Number) storeIdObj).intValue();
                                } else if (storeIdObj instanceof String) {
                                    try {
                                        storeId = Integer.parseInt((String) storeIdObj);
                                    } catch (NumberFormatException e) {
                                        log.debug("无法将storeId转换为Integer: {}", storeIdObj);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.debug("解析店铺分享消息内容失败: text={}", webSocketVo.getText(), e);
            }
            
            // 从senderId获取用户ID
            if (webSocketVo.getSenderId() != null) {
                if (webSocketVo.getSenderId().startsWith("user_")) {
                    // 普通用户分享
                    String phone = webSocketVo.getSenderId().substring(5); // 去掉 "user_" 前缀
                    LambdaQueryWrapper<LifeUser> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(LifeUser::getUserPhone, phone)
                            .eq(LifeUser::getDeleteFlag, 0);
                    LifeUser lifeUser = lifeUserMapper.selectOne(wrapper);
                    if (lifeUser != null && lifeUser.getId() != null) {
                        userId = lifeUser.getId();
                    }
                } else if (webSocketVo.getSenderId().startsWith("store_")) {
                    // 店铺分享（备用逻辑）
                    String phone = webSocketVo.getSenderId().substring(6); // 去掉 "store_" 前缀
                    LambdaQueryWrapper<StoreUser> wrapper = new LambdaQueryWrapper<>();
                    wrapper.eq(StoreUser::getPhone, phone)
                            .eq(StoreUser::getDeleteFlag, 0);
                    StoreUser storeUser = storeUserMapper.selectOne(wrapper);
                    if (storeUser != null) {
                        if (storeUser.getStoreId() != null && storeId == null) {
                            storeId = storeUser.getStoreId();
                        }
                        if (storeUser.getId() != null) {
                            userId = storeUser.getId();
                        }
                    }
                }
            }
            
            // 创建店铺分享埋点事件
            if (storeId != null) {
                StoreTrackEvent trackEvent = new StoreTrackEvent();
                trackEvent.setEventType("SHARE");
                trackEvent.setEventCategory("INTERACTION");
                trackEvent.setStoreId(storeId);
                trackEvent.setUserId(userId);
                trackEvent.setTargetId(storeId);
                trackEvent.setTargetType("STORE");
                trackEvent.setEventTime(new java.util.Date());
                
                // 获取IP地址、User-Agent和设备类型
                Map<String, String> connectionInfo = connectionInfoMap.get(sendId);
                if (connectionInfo != null) {
                    trackEvent.setIpAddress(connectionInfo.get("ipAddress"));
                    trackEvent.setUserAgent(connectionInfo.get("userAgent"));
                    trackEvent.setDeviceType(UserAgentParserUtil.parseDeviceType(connectionInfo.get("userAgent")));
                } else {
                    // 如果Map中没有，尝试从当前实例获取
                    trackEvent.setIpAddress(this.ipAddress);
                    trackEvent.setUserAgent(this.userAgent);
                    trackEvent.setDeviceType(UserAgentParserUtil.parseDeviceType(this.userAgent));
                }
                
                // 异步写入Redis List
                String eventJson = objectMapper.writeValueAsString(trackEvent);
                baseRedisService.setListRight("track:event:queue", eventJson);
                
                log.debug("WebSocket店铺分享埋点记录成功: storeId={}, userId={}, ipAddress={}, userAgent={}", 
                        storeId, userId, trackEvent.getIpAddress(), trackEvent.getUserAgent());
            } else {
                log.warn("WebSocket店铺分享埋点记录失败: 无法获取storeId, senderId={}", webSocketVo.getSenderId());
            }
        } catch (Exception e) {
            log.error("WebSocket店铺分享埋点记录异常: senderId={}, type={}", 
                    webSocketVo.getSenderId(), webSocketVo.getType(), e);
        }
    }

}
