package com.zhentao.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhentao.entity.MatchData;
import com.zhentao.redis.RedisMatchPool;
import com.zhentao.redis.RedisMatchQueue;
import com.zhentao.util.MatchingAlgorithmUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class MatchService {

    // 不同匹配模式的阈值
    private static final double MATCH_THRESHOLD_SMART = 50.0;   // 智能算法：50%
    private static final double MATCH_THRESHOLD_ONLINE = 45.0;  // 实时在线：45%（降低阈值，提高匹配速度）
    private static final double MATCH_THRESHOLD_PRECISE = 70.0; // 精准推荐：70%（提高阈值，更严格筛选）

    private final RedisMatchPool redisMatchPool;
    private final ObjectMapper objectMapper;
    private final RedisMatchQueue matchQueue;
    private final RedisTemplate<String, Object> redisTemplate;
    private final java.util.concurrent.ConcurrentHashMap<String, Long> localLocks = new java.util.concurrent.ConcurrentHashMap<>();

    public MatchService(RedisMatchPool redisMatchPool,
                        ObjectMapper objectMapper, RedisMatchQueue matchQueue,
                        RedisTemplate<String, Object> redisTemplate) {
        this.redisMatchPool = redisMatchPool;
        this.objectMapper = objectMapper;
        this.matchQueue = matchQueue;
        this.redisTemplate = redisTemplate;
    }

    public Map<String, Object> handleMatchRequest(MatchData data) {
        String userId = data.getUserId();
        try {
            String lockKey = "match:lock:" + userId;
            String lockVal = UUID.randomUUID().toString();
            boolean locked = acquireLockWithFallback(lockKey, lockVal, 30_000);
            if (!locked) { 
                return createErrorResponse("您已在匹配中，请稍候"); 
            }
            try {
                boolean added = redisMatchPool.addToPool(data);
                if (!added) { 
                    return createErrorResponse("加入匹配池失败"); 
                }
                try { matchQueue.addToQueue(userId); } catch (Exception ignore) {}
                
                MatchResult r = tryMatch(userId, data);
                if (r.isMatched()) {
                    Map<String, Object> matchInfo = createMatch(userId, r.getMatchedUserId(), r.getMatchScore());
                    return createSuccessResponse("匹配成功", matchInfo);
                } else {
                    return createWaitingResponse("正在等待匹配中...");
                }
            } finally { releaseLockWithFallback(lockKey, lockVal); }
        } catch (Exception e) {
            log.error("handleMatchRequest error", e);
            String msg = e.getMessage();
            if (msg != null && msg.toLowerCase().contains("misconf")) {
                return createErrorResponse("匹配服务繁忙，请稍后再试");
            }
            return createErrorResponse("匹配失败：" + (msg != null ? msg : "未知错误"));
        }
    }

    public MatchResult tryMatch(String userId, MatchData userData) {
        // 获取匹配模式和阈值
        String matchMode = userData.getMatchMode();
        if (matchMode == null || matchMode.isEmpty()) {
            matchMode = "smart"; // 默认智能算法
        }
        double threshold = getMatchThreshold(matchMode);
        
        List<String> online = redisMatchPool.getAllUsersInPool();
        Set<String> unmatched = redisMatchPool.getUserUnmatchedSet(userId);
        Map<String, Double> scores = new HashMap<>();
        
        for (String other : online) {
            if (userId.equals(other)) continue;
            if (unmatched.contains(other)) continue;
            
            MatchData od = redisMatchPool.getUserMatchData(other);
            if (od == null) continue;
            
            // 性别筛选：只匹配异性
            if (!MatchingAlgorithmUtils.isOppositeGender(userData.getGender(), od.getGender())) {
                continue;
            }
            
            // 根据匹配模式进行额外筛选
            if (!shouldMatchByMode(userData, od, matchMode)) {
                continue;
            }
            
            String otherLockKey = "match:lock:" + other;
            String otherLockVal = UUID.randomUUID().toString();
            boolean ok;
            try { ok = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(otherLockKey, otherLockVal, 5, TimeUnit.SECONDS)); }
            catch (Exception ex) { ok = acquireLockWithFallback(otherLockKey, otherLockVal, 5_000); }
            if (!ok) continue;
            
            try {
                if (!redisMatchPool.isUserInPool(other)) continue;
                double score = MatchingAlgorithmUtils.calculateMatchScore(userData, od);
                scores.put(other, score);
                if (score >= threshold) {
                    return new MatchResult(true, other, score);
                }
            } finally { releaseLockWithFallback(otherLockKey, otherLockVal); }
        }
        
        if (!scores.isEmpty()) {
            Map.Entry<String, Double> best = scores.entrySet().stream().max(Map.Entry.comparingByValue()).get();
            redisMatchPool.recordUnmatch(userId, best.getKey());
        }
        return new MatchResult(false, null, 0);
    }
    
    /**
     * 根据匹配模式获取阈值
     */
    private double getMatchThreshold(String matchMode) {
        switch (matchMode.toLowerCase()) {
            case "online":
                return MATCH_THRESHOLD_ONLINE;
            case "precise":
                return MATCH_THRESHOLD_PRECISE;
            case "smart":
            default:
                return MATCH_THRESHOLD_SMART;
        }
    }
    
    /**
     * 根据匹配模式判断是否应该匹配
     */
    private boolean shouldMatchByMode(MatchData userData, MatchData otherData, String matchMode) {
        switch (matchMode.toLowerCase()) {
            case "precise":
                // 精准推荐：更严格的筛选条件
                // 1. 年龄差距不超过10岁
                if (userData.getAge() != null && otherData.getAge() != null) {
                    int ageDiff = Math.abs(userData.getAge() - otherData.getAge());
                    if (ageDiff > 10) {
                        return false;
                    }
                }
                // 2. 地理位置距离不超过50公里
                if (userData.getLatitude() != null && userData.getLongitude() != null &&
                    otherData.getLatitude() != null && otherData.getLongitude() != null) {
                    double distance = calculateDistance(
                        userData.getLatitude(), userData.getLongitude(),
                        otherData.getLatitude(), otherData.getLongitude()
                    );
                    if (distance > 50) {
                        return false;
                    }
                }
                // 3. 至少有一个共同兴趣
                if (userData.getInterests() != null && otherData.getInterests() != null &&
                    !userData.getInterests().isEmpty() && !otherData.getInterests().isEmpty()) {
                    boolean hasCommonInterest = userData.getInterests().stream()
                        .anyMatch(otherData.getInterests()::contains);
                    if (!hasCommonInterest) {
                        return false;
                    }
                }
                return true;
                
            case "online":
                // 实时在线：只匹配当前在匹配池中的用户（已经在池中，无需额外筛选）
                return true;
                
            case "smart":
            default:
                // 智能算法：使用默认算法，无额外筛选
                return true;
        }
    }
    
    /**
     * 计算两点间距离（公里）
     */
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        final int R = 6371; // 地球半径（公里）
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                   Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
                   Math.sin(dLon/2) * Math.sin(dLon/2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        return R * c;
    }

    public Map<String, Object> createMatch(String u1, String u2, double score) {
        try {
            String roomId = UUID.randomUUID().toString();
            
            // 先获取用户信息（在移除之前）
            Map<String, Object> user1Info = buildUserInfo(u1);
            Map<String, Object> user2Info = buildUserInfo(u2);
            
            log.info("创建匹配: u1={}, u2={}, score={}", u1, u2, score);
            log.info("用户1信息: {}", user1Info);
            log.info("用户2信息: {}", user2Info);
            
            // 确保用户信息不为空
            if (user1Info.isEmpty() || user2Info.isEmpty()) {
                log.warn("用户信息为空，使用默认值");
                if (user1Info.isEmpty()) {
                    user1Info.put("userId", u1);
                    user1Info.put("nickname", "用户" + u1);
                }
                if (user2Info.isEmpty()) {
                    user2Info.put("userId", u2);
                    user2Info.put("nickname", "用户" + u2);
                }
            }
            
            // 再移除用户
            redisMatchPool.removeFromPool(u1);
            redisMatchPool.removeFromPool(u2);
            matchQueue.removeFromQueue(u1);
            matchQueue.removeFromQueue(u2);
            
            Map<String,Object> matchInfo = new HashMap<>();
            matchInfo.put("roomId", roomId);
            matchInfo.put("matchScore", score);
            matchInfo.put("user1Id", u1);
            matchInfo.put("user2Id", u2);
            matchInfo.put("user1Info", user1Info);
            matchInfo.put("user2Info", user2Info);
            matchInfo.put("matchTime", System.currentTimeMillis());
            
            log.info("匹配信息已创建: {}", matchInfo);
            
            return matchInfo;
        } catch (Exception e) {
            log.error("createMatch error", e);
            // 返回一个包含基本信息的Map，而不是空Map
            Map<String, Object> errorMatchInfo = new HashMap<>();
            errorMatchInfo.put("roomId", UUID.randomUUID().toString());
            errorMatchInfo.put("matchScore", 0.0);
            errorMatchInfo.put("user1Id", u1);
            errorMatchInfo.put("user2Id", u2);
            errorMatchInfo.put("user1Info", buildUserInfo(u1));
            errorMatchInfo.put("user2Info", buildUserInfo(u2));
            errorMatchInfo.put("matchTime", System.currentTimeMillis());
            return errorMatchInfo;
        }
    }

    private Map<String, Object> buildUserInfo(String userId) {
        MatchData data = redisMatchPool.getUserMatchData(userId);
        Map<String,Object> m = new HashMap<>();
        if (data != null) {
            m.put("userId", data.getUserId());
            m.put("nickname", data.getNickname());
            m.put("gender", data.getGender());
            m.put("age", data.getAge());
            m.put("avatarUrl", data.getAvatarUrl());
            m.put("interests", data.getInterests());
            m.put("city", data.getCity());
            m.put("introduction", data.getIntroduction());
        }
        return m;
    }

    public boolean removeFromMatchPool(String userId) {
        boolean a = redisMatchPool.removeFromPool(userId);
        boolean b = matchQueue.removeFromQueue(userId);
        return a || b;
    }

    private void releaseLock(String key, String expect) {
        try {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(key), expect);
        } catch (Exception ignore) {}
    }

    private boolean acquireLockWithFallback(String key, String val, long ttlMs) {
        try {
            Boolean ok = redisTemplate.opsForValue().setIfAbsent(key, val, ttlMs, TimeUnit.MILLISECONDS);
            return Boolean.TRUE.equals(ok);
        } catch (Exception e) {
            long now = System.currentTimeMillis();
            Long expireAt = localLocks.putIfAbsent(key, now + ttlMs);
            if (expireAt == null) return true;
            if (expireAt < now) { localLocks.put(key, now + ttlMs); return true; }
            return false;
        }
    }

    private void releaseLockWithFallback(String key, String val) {
        try { releaseLock(key, val); }
        catch (Exception ignore) { localLocks.remove(key); }
    }

    private Map<String, Object> createSuccessResponse(String message, Map<String, Object> data) {
        Map<String, Object> response = new HashMap<>();
        response.put("code", 200);
        response.put("msg", message);
        response.put("status", "success");
        response.put("data", data);
        return response;
    }

    private Map<String, Object> createWaitingResponse(String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("code", 200);
        response.put("msg", message);
        response.put("status", "waiting");
        return response;
    }

    private Map<String, Object> createErrorResponse(String message) {
        Map<String, Object> response = new HashMap<>();
        response.put("code", 500);
        response.put("msg", message);
        response.put("status", "error");
        return response;
    }

    public static class MatchResult {
        private final boolean matched;
        private final String matchedUserId;
        private final double matchScore;

        public MatchResult(boolean matched, String matchedUserId, double matchScore) {
            this.matched = matched; this.matchedUserId = matchedUserId; this.matchScore = matchScore;
        }
        public boolean isMatched() { return matched; }
        public String getMatchedUserId() { return matchedUserId; }
        public double getMatchScore() { return matchScore; }
    }
}

