package com.ruoyi.cai.rank;

import com.ruoyi.cai.constant.RedisConstant;
import com.ruoyi.cai.mq.AmqpProducer;
import com.ruoyi.cai.mq.handle.dto.RankDTO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RScoredSortedSet;
import org.redisson.api.RedissonClient;
import org.redisson.client.protocol.ScoredEntry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Component
@Slf4j
public class RankManager {

    @Autowired
    private RedissonClient redissonClient;

    public static final String LOVE_KEY_FORMAT = RedisConstant.LOVE_RANK_REDIS;
    public static final String INVITE_KEY_FORMAT = RedisConstant.INVITE_RANK_REDIS;

    public static final String DAY = "Day";
    public static final String WEEK = "Week";
    public static final String MONTH = "Month";
    public static final String TOTAL = "Total";

    private String dateString(LocalDate time){
        return time.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    }

    private String getKeyInviteDay(LocalDate time){
        String day = dateString(time);
        return String.format(INVITE_KEY_FORMAT,DAY,day);
    }

    private String getKeyInviteWeek(LocalDate time){
        LocalDate monday = time.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        LocalDate sunday = time.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
        String mondayStr = dateString(monday);
        String sundayStr = dateString(sunday);
        return String.format(INVITE_KEY_FORMAT,WEEK,mondayStr+"-"+sundayStr);
    }

    private String getKeyInviteMonth(LocalDate time){
        String month = time.format(DateTimeFormatter.ofPattern("yyyyMM"));
        return String.format(INVITE_KEY_FORMAT,MONTH,month);
    }

    private String getKeyInviteTotal(){
        return String.format(INVITE_KEY_FORMAT,TOTAL,TOTAL);
    }

    private String getKeyLoveDay(LocalDate time){
        String day = dateString(time);
        return String.format(LOVE_KEY_FORMAT,DAY,day);
    }

    private String getKeyLoveWeek(LocalDate time){
        LocalDate monday = time.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        LocalDate sunday = time.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
        String mondayStr = dateString(monday);
        String sundayStr = dateString(sunday);
        return String.format(LOVE_KEY_FORMAT,WEEK,mondayStr+"-"+sundayStr);
    }

    private String getKeyLoveMonth(LocalDate time){
        String month = time.format(DateTimeFormatter.ofPattern("yyyyMM"));
        return String.format(LOVE_KEY_FORMAT,MONTH,month);
    }

    private String getKeyLoveTotal(){
        return String.format(LOVE_KEY_FORMAT,TOTAL,TOTAL);
    }

    public void addRank(RankDTO dto){
        if(dto.getRankType() == null){
            return;
        }
        if(dto.getRankType() == 1){
            LocalDate localDate = dto.getHappenTime().toLocalDate();
            String dayKey = getKeyLoveDay(localDate);
            RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(dayKey);
            daySet.addScore(dto.getUserId()+"",dto.getPrice());

            String weekKey = getKeyLoveWeek(localDate);
            RScoredSortedSet<String> weekSet = redissonClient.getScoredSortedSet(weekKey);
            weekSet.addScore(dto.getUserId()+"",dto.getPrice());

            String monthKey = getKeyLoveMonth(localDate);
            RScoredSortedSet<String> monthSet = redissonClient.getScoredSortedSet(monthKey);
            monthSet.addScore(dto.getUserId()+"",dto.getPrice());

            String totalKey = getKeyLoveTotal();
            RScoredSortedSet<String> totalSet = redissonClient.getScoredSortedSet(totalKey);
            totalSet.addScore(dto.getUserId()+"",dto.getPrice());
        }else if(dto.getRankType() == 2){
            LocalDate localDate = dto.getHappenTime().toLocalDate();
            String dayKey = getKeyInviteDay(localDate);
            RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(dayKey);
            daySet.addScore(dto.getUserId()+"",dto.getPrice());

            String weekKey = getKeyInviteWeek(localDate);
            RScoredSortedSet<String> weekSet = redissonClient.getScoredSortedSet(weekKey);
            weekSet.addScore(dto.getUserId()+"",dto.getPrice());

            String monthKey = getKeyInviteMonth(localDate);
            RScoredSortedSet<String> monthSet = redissonClient.getScoredSortedSet(monthKey);
            monthSet.addScore(dto.getUserId()+"",dto.getPrice());

            String totalKey = getKeyInviteTotal();
            RScoredSortedSet<String> totalSet = redissonClient.getScoredSortedSet(totalKey);
            totalSet.addScore(dto.getUserId()+"",dto.getPrice());
        }

    }

    public List<RankNode> getLoveRankLastDay(int limit){
        return getLoveRankDay(LocalDate.now().plusDays(-1),limit);
    }
    public List<RankNode> getLoveRankLastWeek(int limit){
        return getLoveRankWeek(LocalDate.now().plusWeeks(-7),limit);
    }

    public List<RankNode> getLoveRankToday(int limit){
        return getLoveRankDay(LocalDate.now(),limit);
    }
    public List<RankNode> getLoveRankWeek(int limit){
        return getLoveRankWeek(LocalDate.now(),limit);
    }
    public List<RankNode> getLoveRankMonth(int limit){
        return getLoveRankMonth(LocalDate.now(),limit);
    }

    public List<RankNode> getLoveRankDay(LocalDate date, int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyLoveDay(date);
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getLoveRankWeek(LocalDate date,int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyLoveWeek(date);
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getLoveRankMonth(LocalDate date,int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyLoveMonth(date);
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getLoveRankTotal(int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyLoveTotal();
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getInviteRankLastDay(int limit){
        return getInviteRankDay(LocalDate.now().plusDays(-1),limit);
    }
    public List<RankNode> getInviteRankLastWeek(int limit){
        return getInviteRankWeek(LocalDate.now().plusWeeks(-7),limit);
    }

    public List<RankNode> getInviteRankToday(int limit){
        return getInviteRankDay(LocalDate.now(),limit);
    }
    public List<RankNode> getInviteRankWeek(int limit){
        return getInviteRankWeek(LocalDate.now(),limit);
    }
    public List<RankNode> getInviteRankMonth(int limit){
        return getInviteRankMonth(LocalDate.now(),limit);
    }

    public List<RankNode> getInviteRankDay(LocalDate date, int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyInviteDay(date);
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getInviteRankWeek(LocalDate date,int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyInviteWeek(date);
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getInviteRankMonth(LocalDate date,int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyInviteMonth(date);
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }

    public List<RankNode> getInviteRankTotal(int limit){
        List<RankNode> res = new ArrayList<>();
        String key = getKeyInviteTotal();
        RScoredSortedSet<String> daySet = redissonClient.getScoredSortedSet(key);
        Collection<ScoredEntry<String>> entries = daySet.entryRangeReversed(0, limit-1);
        for (ScoredEntry<String> entry : entries) {
            res.add(RankNode.getNode(entry));
        }
        return res;
    }


    @Autowired
    private AmqpProducer amqpProducer;

    public void sendLoveRankMq(Long userId,Long value,Long traceId){
        try {
            RankDTO rank = new RankDTO();
            rank.setUserId(userId);
            rank.setPrice(value);
            rank.setTraceId(traceId);
            rank.setRankType(1);
            amqpProducer.sendCommonMq(rank);
        }catch (Exception e){
            log.error("发送魅力榜 rank mq失败",e);
        }
    }

    public void sendInviteRankMq(Long userId,Long value,Long traceId){
        try {
            RankDTO rank = new RankDTO();
            rank.setUserId(userId);
            rank.setPrice(value);
            rank.setTraceId(traceId);
            rank.setRankType(2);
            amqpProducer.sendCommonMq(rank);
        }catch (Exception e){
            log.error("发送邀请榜 rank mq失败",e);
        }
    }
}
