package com.example.demo.module.rank;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.common.GlobalRoot;
import com.example.demo.module.user.Player;
import com.example.demo.table.UserFriend;
import pers.jc.engine.JCEngine;
import pers.jc.network.SocketComponent;
import pers.jc.network.SocketMethod;
import pers.jc.network.SocketResponse;
import pers.jc.sql.SQL;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Socket组件-排行
 */
@SocketComponent("rankComp")
public class RankComp {

    @SocketMethod(async = true)
    public void getFriendRankList(Player player, SocketResponse response) {
        List<UserFriend> userFriendList = GlobalRoot.db.select(UserFriend.class, new SQL(){{
            WHERE("myID = " + player.userID);
            LIMIT("50");
        }});
        List<String> userIdList = new LinkedList<>();
        userIdList.add(String.valueOf(player.userID));
        for (UserFriend uf : userFriendList) {
            userIdList.add(String.valueOf(uf.getFriendID()));
        }
        List<JSONObject> list = GlobalRoot.db.select(new SQL(){{
            SELECT("id");
            SELECT("IFNULL(score," + RankService.ins.getDefaultPkScore() + ") as score");
            SELECT("avatarID");
            SELECT("avatarUrl");
            SELECT("nickname");
            SELECT("offlineTime");
            FROM("user_info");
            LEFT_OUTER_JOIN("game9_score on id = userID");
            WHERE("id in (" + String.join(",", userIdList) + ")");
            ORDER_BY("score DESC, update_time ASC, id ASC");
        }});
        int rankNum = 0;
        for (JSONObject jo: list) {
            jo.put("rankNum", ++rankNum);
        }
        JCEngine.gameService.execute(() -> {
            for (JSONObject jo: list) {
                int id = jo.getInteger("id");
                jo.put("online", Player.playerMap.get(id) == null ? false : true);
            }
            response.send(list);
        });
    }

    @SocketMethod(async = true)
    public void getRankList(
            Player player, SocketResponse socketResponse, int rankArea, int rankGameType,
            String countryCode, String stateCode, String cityCode
        ) {
        SQL sqlUtil = new SQL();
        String formatScoreSQL = "IFNULL(score," + RankService.ins.getDefaultPkScore() + ") as score";
        String sqlRankList = "SELECT id, " + formatScoreSQL + ", avatarID, avatarUrl, nickname, country FROM user_info LEFT JOIN game"
                + rankGameType + "_score ON userID = id";
        if (rankArea == 3) { //国排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
        } else if (rankArea == 1) { //省排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND state = " + sqlUtil.PARAM(stateCode);
        } else if (rankArea == 2) { //市排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND state = " + sqlUtil.PARAM(stateCode);
            sqlRankList += " AND city = " + sqlUtil.PARAM(cityCode);
        } else {
            //否则为全球排行
        }
        sqlRankList += " ORDER BY score DESC, update_time ASC, id ASC";
        List<JSONObject> rankList = GlobalRoot.db.select(sqlRankList + " Limit 20");
        for (int i = 0; i < rankList.size(); i++) {
            rankList.get(i).put("rankNum", i + 1);
        }
        String sqlMyRank = "SELECT id, score, avatarID, avatarUrl, nickname, country, rankNum FROM\n" +
                " (SELECT id, score, avatarID, avatarUrl, nickname, country, @rankNum:=@rankNum+1 AS rankNum\n" +
                " FROM (" + sqlRankList + ") a, (SELECT @rankNum:=0) b) c" +
                " WHERE id = " + sqlUtil.PARAM(player.userID);
        List<JSONObject> myRank = GlobalRoot.db.select(sqlMyRank);
        myRank.forEach((a) -> {
            a.put("userID", a.get("id"));
        });
        socketResponse.send(rankList, myRank);
    }

    List<Integer> pkGameTypes = Arrays.asList(new Integer[]{9, 10, 11, 12});
    //PK游戏结果，0：玩家1胜利；1：玩家2胜利；2：双方平局
    List<Integer> pkGameResults = Arrays.asList(new Integer[]{0, 1, 2});
    @SocketMethod(async = true)
    public void uploadPKGameRes(String roomKey, int gameType,
            int p1ID, int p2ID, int gameRes
    ) {
        if (roomKey == null || roomKey.length() != 32) return;
        if (!pkGameTypes.contains(gameType)) return;
        if (!pkGameResults.contains(gameRes)) return;
        boolean res = RankService.ins.insertRoomGameRecord(roomKey, gameType, p1ID, p2ID, gameRes);
        if (!res) return;
        if (!RankService.ins.isRoomNeedCalculatePKScore(roomKey)) return;
        double Ra = RankService.ins.getGameScore(gameType, p1ID);
        double Rb = RankService.ins.getGameScore(gameType, p2ID);
        double Ea = 1 / ( 1 + Math.pow(10, (Rb-Ra)/400d) );
        double Eb = 1 / ( 1 + Math.pow(10, (Ra-Rb)/400d) );
        double Sa;
        double Sb;
        double Ka;
        double Kb;
        if (gameRes == 0) {
            Sa = 1;
            Sb = 0;
            Ka = 32;
            Kb = Rb < 1100 ? 12 : 32;
        } else if (gameRes == 1) {
            Sa = 0;
            Sb = 1;
            Ka = Ra < 1100 ? 12 : 32;
            Kb = 32;
        } else {
            Sa = 0.5;
            Sb = 0.5;
            Ka = 22;
            Kb = 22;
        }
        Ra += Ka * (Sa - Ea);
        Rb += Kb * (Sb - Eb);
        RankService.ins.updateGameScore(gameType, p1ID, (int)Ra);
        RankService.ins.updateGameScore(gameType, p2ID, (int)Rb);
    }
    //FruitMaster:15,WildAttack:14,DuckHunter:13
    List<Integer> singleGameTypes = Arrays.asList(new Integer[]{15, 14, 13});

    //上传用户单机游戏分数
    @SocketMethod(async = true)
    public void uploadSinglePlayerGameRes(
            Player player, int gameType,int score
    ) {

        if (!singleGameTypes.contains(gameType)) return;
        //只加一个分数
        double Ra = RankService.ins.getSingleGameScore(gameType, player.userID);
        Ra += score ;
        RankService.ins.updateGameScore(gameType, player.userID, (int)Ra);
    }

    @SocketMethod(async = true)
    public void getRankListByGameType(
            Player player, SocketResponse socketResponse, int rankArea, int rankGameType,
            String countryCode, String stateCode, String cityCode
    ) {
        SQL sqlUtil = new SQL();
        int _score = singleGameTypes.contains(rankGameType) ? RankService.ins.getDefaultSingleScore() : RankService.ins.getDefaultPkScore();
        String formatScoreSQL = "IFNULL(score," + _score + ") as score";
        String sqlRankList = "SELECT id, " + formatScoreSQL + ", avatarID, avatarUrl, nickname, country FROM user_info LEFT JOIN game"
                + rankGameType + "_score ON userID = id";
        if (rankArea == 3) { //国排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
        } else if (rankArea == 1) { //省排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND state = " + sqlUtil.PARAM(stateCode);
        } else if (rankArea == 2) { //市排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND state = " + sqlUtil.PARAM(stateCode);
            sqlRankList += " AND city = " + sqlUtil.PARAM(cityCode);
        } else {
            //否则为全球排行
        }
        sqlRankList += " ORDER BY score DESC, update_time ASC, id ASC";
        List<JSONObject> rankList = GlobalRoot.db.select(sqlRankList + " Limit 50");
        for (int i = 0; i < rankList.size(); i++) {
            rankList.get(i).put("rankNum", i + 1);
        }
        String sqlMyRank = "SELECT id, score, avatarID, avatarUrl, nickname, country, rankNum FROM\n" +
                " (SELECT id, score, avatarID, avatarUrl, nickname, country, @rankNum:=@rankNum+1 AS rankNum\n" +
                " FROM (" + sqlRankList + ") a, (SELECT @rankNum:=0) b) c" +
                " WHERE id = " + sqlUtil.PARAM(player.userID);
        List<JSONObject> myRank = GlobalRoot.db.select(sqlMyRank);
        myRank.forEach((a) -> {
            a.put("userID", a.get("id"));
        });
        socketResponse.send(rankList, myRank);
    }

    /**
     * 处理赛季排行，按月区分
     */

    //上传分数部分
    @SocketMethod(async = true)
    public void uploadSeasonPKGameRes(String roomKey, int gameType,
                                int p1ID, int p2ID, int gameRes
    ) {
        if (roomKey == null || roomKey.length() != 32) return;
        if (!pkGameTypes.contains(gameType)) return;
        if (!pkGameResults.contains(gameRes)) return;
        boolean res = RankService.ins.insertRoomGameRecord(roomKey, gameType, p1ID, p2ID, gameRes);
        if (!res) return;
        if (!RankService.ins.isRoomNeedCalculatePKScore(roomKey)) return;

        //获取时间
        String[] dates = getCurrentDates();
        String startDateStr = dates[0];
        String endDateStr = dates[1];

        Map map1 = RankService.ins.getSeasonGameScore(gameType, p1ID, startDateStr, endDateStr);
        Map map2 =  RankService.ins.getSeasonGameScore(gameType, p2ID, startDateStr, endDateStr);
        int id1 = (int)map1.get("id");
        int id2 = (int)map2.get("id");

        double Ra = (int)map1.get("score"); //RankService.ins.getSeasonGameScore(gameType, p1ID, startDateStr, endDateStr);
        double Rb = (int)map2.get("score");// RankService.ins.getSeasonGameScore(gameType, p2ID, startDateStr, endDateStr);

        double Ea = 1 / ( 1 + Math.pow(10, (Rb-Ra)/400d) );
        double Eb = 1 / ( 1 + Math.pow(10, (Ra-Rb)/400d) );
        double Sa;
        double Sb;
        double Ka;
        double Kb;
        if (gameRes == 0) {
            Sa = 1;
            Sb = 0;
            Ka = 32;
            Kb = Rb < 1100 ? 12 : 32;
        } else if (gameRes == 1) {
            Sa = 0;
            Sb = 1;
            Ka = Ra < 1100 ? 12 : 32;
            Kb = 32;
        } else {
            Sa = 0.5;
            Sb = 0.5;
            Ka = 22;
            Kb = 22;
        }
        Ra += Ka * (Sa - Ea);
        Rb += Kb * (Sb - Eb);
        RankService.ins.updateSeasonGameScore(gameType,id1, p1ID, (int)Ra);
        RankService.ins.updateSeasonGameScore(gameType,id2, p2ID, (int)Rb);
    }
    //上传用户单机游戏分数
    @SocketMethod(async = true)
    public void uploadSeasonSinglePlayerGameRes(
            Player player, int gameType,int score
    ) {

        if (!singleGameTypes.contains(gameType)) return;
        //只加一个分数
        //获取时间
        String[] dates = getCurrentDates();
        String startDateStr = dates[0];
        String endDateStr = dates[1];
        Map map1 =  RankService.ins.getSingleSeasonGameScore(gameType, player.userID, startDateStr, endDateStr);
        int id1 = (int)map1.get("id");
        int Ra = (int)map1.get("score"); //RankService.ins.getGameScore(gameType, player.userID);
        //Ra += score ;
        //记录一个最高分数
        if(score > Ra) Ra = score;
        RankService.ins.updateSeasonGameScore(gameType, id1 , player.userID, Ra);
    }

    List<Integer> allGameTypes = Arrays.asList(new Integer[]{9, 10, 11, 12,13,14,15});
    //获取赛季排行版
    @SocketMethod(async = true)
    public void getSeasonRankListByGameType(
            Player player, SocketResponse socketResponse, int rankArea, int rankGameType,
            String countryCode, String stateCode, String cityCode
    ) {
        if (!allGameTypes.contains(rankGameType)) return;
        //获取时间
        String[] dates = getCurrentDates();
        String startDateStr = dates[0];
        String endDateStr = dates[1];

        SQL sqlUtil = new SQL();
        //singleGameTypes.contains(rankGameType) ? 0 :
        //int _score =  RankService.ins.getDefaultPkScore();
        int _score = singleGameTypes.contains(rankGameType) ? RankService.ins.getDefaultSingleScore() : RankService.ins.getDefaultPkScore();
        String formatScoreSQL = "IFNULL(score," + _score + ") as score";
        String sqlRankList = "SELECT user_info.id, " + formatScoreSQL
                + ", avatarID, avatarUrl, nickname, country FROM user_info LEFT JOIN game_season_"
                + rankGameType + " ON userID = user_info.id";

        if (rankArea == 3) { //国排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND create_time >= '"+startDateStr+"' AND create_time <= '" + endDateStr + "'";
        } else if (rankArea == 1) { //省排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND state = " + sqlUtil.PARAM(stateCode);
            sqlRankList += " AND create_time >= '"+startDateStr+"' AND create_time <= '" + endDateStr + "'";
        } else if (rankArea == 2) { //市排行
            sqlRankList += " WHERE country = " + sqlUtil.PARAM(countryCode);
            sqlRankList += " AND state = " + sqlUtil.PARAM(stateCode);
            sqlRankList += " AND city = " + sqlUtil.PARAM(cityCode);
            sqlRankList += " AND create_time >= '"+startDateStr+"' AND create_time <= '" + endDateStr + "'";
        } else {
            //否则为全球排行
            sqlRankList += " WHERE create_time >= '"+startDateStr+"' AND create_time <= '" + endDateStr + "'";
        }

        sqlRankList += " ORDER BY score DESC, update_time ASC, id ASC";
        List<JSONObject> rankList = GlobalRoot.db.select(sqlRankList + " Limit 20");
        for (int i = 0; i < rankList.size(); i++) {
            rankList.get(i).put("rankNum", i + 1);
        }
        String sqlMyRank = "SELECT id, score, avatarID, avatarUrl, nickname, country, rankNum FROM\n" +
                " (SELECT id, score, avatarID, avatarUrl, nickname, country, @rankNum:=@rankNum+1 AS rankNum\n" +
                " FROM (" + sqlRankList + ") a, (SELECT @rankNum:=0) b) c" +
                " WHERE id = " + sqlUtil.PARAM(player.userID);
        List<JSONObject> myRank = GlobalRoot.db.select(sqlMyRank);
        myRank.forEach((a) -> {
            a.put("userID", a.get("id"));
        });
        String seasonName = "S";
        socketResponse.send(rankList, myRank, seasonName,new String[]{startDateStr,endDateStr});
    }



    ////上传分数测试用，限制特定账号
    //@SocketMethod(async = true)
    //public void uploadSeasonPKGameResTest( int gameType,int p1ID, int p2ID, int gameRes
    //) {
    //    //限定两个测试账号
    //    if(p1ID != 178 || p2ID != 281) return;
    //
    //    LocalDate now = LocalDate.now(); // 当前日期
    //    DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    //
    //    String[] dates = getCurrentDates();
    //    String startDateStr = dates[0];
    //    String endDateStr = dates[1];
    //    JCLogger.info("uploadSeasonPKGameResTest","p1:"+p1ID+"，p2:"+p2ID+ "now: " + now.format(formatter1) +",Start: " + startDateStr + "，End: " + endDateStr);
    //    Map map1 = RankService.ins.getSeasonGameScore(gameType, p1ID, startDateStr, endDateStr);
    //    Map map2 =  RankService.ins.getSeasonGameScore(gameType, p2ID, startDateStr, endDateStr);
    //    int id1 = (int)map1.get("id");
    //    int id2 = (int)map2.get("id");
    //
    //    double Ra = (int)map1.get("score"); //RankService.ins.getSeasonGameScore(gameType, p1ID, startDateStr, endDateStr);
    //    double Rb = (int)map2.get("score");// RankService.ins.getSeasonGameScore(gameType, p2ID, startDateStr, endDateStr);
    //
    //    double Ea = 1 / ( 1 + Math.pow(10, (Rb-Ra)/400d) );
    //    double Eb = 1 / ( 1 + Math.pow(10, (Ra-Rb)/400d) );
    //    double Sa;
    //    double Sb;
    //    double Ka;
    //    double Kb;
    //    if (gameRes == 0) {
    //        Sa = 1;
    //        Sb = 0;
    //        Ka = 32;
    //        Kb = Rb < 1100 ? 12 : 32;
    //    } else if (gameRes == 1) {
    //        Sa = 0;
    //        Sb = 1;
    //        Ka = Ra < 1100 ? 12 : 32;
    //        Kb = 32;
    //    } else {
    //        Sa = 0.5;
    //        Sb = 0.5;
    //        Ka = 22;
    //        Kb = 22;
    //    }
    //    Ra += Ka * (Sa - Ea);
    //    Rb += Kb * (Sb - Eb);
    //    RankService.ins.updateSeasonGameScore(gameType,id1, p1ID, (int)Ra);
    //    RankService.ins.updateSeasonGameScore(gameType,id2, p2ID, (int)Rb);
    //}

    /**
     * 获取当前月份，返回当月第一天和最后一天
     * @return
     */
    String[] getCurrentDates(){
        LocalDate now = LocalDate.now(); // 当前日期
        LocalDate firstDayOfMonth = now.withDayOfMonth(1); // 当前月的第一天
        LocalDate lastDayOfMonth = now.withDayOfMonth(now.lengthOfMonth()); // 当前月的最后一天

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String startDateString = firstDayOfMonth.format(formatter);
        String endDateString = lastDayOfMonth.format(formatter);

        // 打印时间段
        //JCLogger.info("dates", "now: " + now.format(formatter) + "，Start: " + startDateString + "，End: " + endDateString);
        return new String[]{startDateString,endDateString};

    }
}
