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 = 50.0;

    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) {
        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;
            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 >= MATCH_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);
    }

    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);
            
            // 再移除用户
            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);
            return new HashMap<>();
        }
    }

    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; }
    }
}

