package com.zhentao.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Component
public class RedisMatchQueue {
    private static final String MATCH_QUEUE_KEY = "match:queue";
    private static final int QUEUE_EXPIRE_HOURS = 24;

    private final RedisTemplate<String, Object> redisTemplate;
    // local fallback queue
    private final Queue<String> localQueue = new ConcurrentLinkedQueue<>();

    public RedisMatchQueue(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public boolean addToQueue(String userId) {
        try {
            double score = System.currentTimeMillis();
            Boolean ok = redisTemplate.opsForZSet().add(MATCH_QUEUE_KEY, userId, score);
            redisTemplate.expire(MATCH_QUEUE_KEY, QUEUE_EXPIRE_HOURS, TimeUnit.HOURS);
            return Boolean.TRUE.equals(ok);
        } catch (Exception e) { 
            if (isMisconf(e)) {
                log.warn("addToQueue MISCONF, fallback to local queue");
            } else {
                log.error("addToQueue error", e);
            }
            if (!localQueue.contains(userId)) localQueue.offer(userId);
            return true;
        }
    }

    public boolean increaseUserPriority(String userId, double inc) {
        try {
            Double curr = redisTemplate.opsForZSet().score(MATCH_QUEUE_KEY, userId);
            if (curr == null) return false;
            redisTemplate.opsForZSet().incrementScore(MATCH_QUEUE_KEY, userId, -inc);
            return true;
        } catch (Exception e) { log.error("increaseUserPriority error", e); return false; }
    }

    public String getHighestPriorityUser() {
        try {
            Set<Object> res = redisTemplate.opsForZSet().range(MATCH_QUEUE_KEY, 0, 0);
            if (res == null || res.isEmpty()) return null;
            return res.iterator().next().toString();
        } catch (Exception e) { log.error("getHighestPriorityUser error", e); return null; }
    }

    public List<String> getTopPriorityUsers(int count) {
        try {
            Set<Object> res = redisTemplate.opsForZSet().range(MATCH_QUEUE_KEY, 0, count - 1);
            if (res == null || res.isEmpty()) return Collections.emptyList();
            return res.stream().map(Object::toString).collect(Collectors.toList());
        } catch (Exception e) { 
            if (isMisconf(e)) {
                log.warn("getTopPriorityUsers MISCONF, use local queue");
            } else {
                log.error("getTopPriorityUsers error", e);
            }
            List<String> list = new ArrayList<>();
            for (int i=0; i<count; i++) {
                String v = localQueue.poll(); if (v==null) break; list.add(v);
            }
            return list;
        }
    }

    public boolean removeFromQueue(String userId) {
        try {
            Long removed = redisTemplate.opsForZSet().remove(MATCH_QUEUE_KEY, userId);
            boolean ok = removed != null && removed > 0;
            localQueue.remove(userId);
            return ok;
        } catch (Exception e) { 
            if (isMisconf(e)) {
                log.warn("removeFromQueue MISCONF, local queue only");
            } else {
                log.error("removeFromQueue error", e);
            }
            localQueue.remove(userId);
            return true; 
        }
    }

    public boolean isUserInQueue(String userId) {
        try { return redisTemplate.opsForZSet().score(MATCH_QUEUE_KEY, userId) != null; }
        catch (Exception e) { 
            if (isMisconf(e)) {
                log.warn("isUserInQueue MISCONF, use local queue");
            } else {
                log.error("isUserInQueue error", e);
            }
            return localQueue.contains(userId); 
        }
    }

    public long getQueueSize() {
        try {
            Long size = redisTemplate.opsForZSet().size(MATCH_QUEUE_KEY);
            return size != null ? size : 0;
        } catch (Exception e) { 
            if (isMisconf(e)) {
                log.warn("getQueueSize MISCONF, use local size");
            } else {
                log.error("getQueueSize error", e);
            }
            return localQueue.size(); 
        }
    }

    private boolean isMisconf(Exception e) {
        String msg = e == null ? null : e.getMessage();
        return msg != null && msg.toLowerCase().contains("misconf");
    }
}

