use crate::commons::utils::{self, CONFIG as config};
use crate::db::models::game::Game;
use crate::db::models::log::Log;
use crate::db::models::manager::*;
use crate::db::models::room::GameRoom;
use bytes::Bytes;
use dashmap::DashMap;
use dashmap::mapref::entry::Entry;
use salvo::prelude::*;
use serde_json::{Value, json};
use socketioxide::SocketIo;
use socketioxide::extract::{Data, SocketRef};
use socketioxide::socket::DisconnectReason;
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};

#[derive(Default)]
struct AdminSession {
    current: Option<(String, SocketRef)>,
    version: u64,
}

lazy_static::lazy_static! {
    /// 当前唯一在线管理员：(token, socket)。
    static ref CLIENT_ADMIN_SESSION: std::sync::Mutex<AdminSession> = std::sync::Mutex::new(AdminSession::default());
    static ref CLIENT_PLAYER_MAP: DashMap<String, (i32,SocketRef)> = DashMap::new(); // 1: 正在游戏中
    static ref CLIENT_TOP_RANKED_MAP: DashMap<String, bool> = DashMap::new();
    static ref CLIENT_AUTH_TOKEN_MAP: DashMap<String, String> = DashMap::new();
    static ref SAVED_GAME_RECORD_MAP: DashMap<String, bool> = DashMap::new();
    static ref ROOM_STATE: std::sync::Mutex<RoomState> = std::sync::Mutex::new(RoomState::default());
}

static GAME_ROUND_ID: AtomicU64 = AtomicU64::new(0);

///房间进入等待最大用户数
const ROOM_ENTRY_LIMIT: usize = 20;
///游戏结束排序最大用户数
const ROOM_SCORE_RANK_LIMIT: usize = 10;
const GAME_START_COUNTDOWN_SECS: u64 = 3;
const GAME_RESULT_WAIT_SECS: u64 = 5;
const GAME_RUNNING_SECS: u64 = 60;
//管理员退出5分钟后,关闭所有玩家的连接
const ADMIN_ABSENT_DEACTIVATE_PLAYER_SECS: u64 = 5 * 60;

// fn get_game_tilte(key: &str) -> String {
//     let mut GAME_MAP = HashMap::new();
//     GAME_MAP.insert("game1", "击鼓齐福");
//     GAME_MAP.insert("game2", "聚宝接福");
//     GAME_MAP.insert("game3", "马上有福");
//     GAME_MAP.insert("game4", "策马迎福");
//     GAME_MAP.insert("game5", "圈彩纳福");
//     GAME_MAP.insert("game6", "福运当头");
//     let str = GAME_MAP.get(key).map_or("", |v| v);
//     str.to_string()
// }

#[derive(Clone, Default)]
struct RoomPlayer {
    /// 成绩分数，具体含义由前端和游戏逻辑自行定义，后端只负责存储和排序。
    score: i64,
    // /// wechat原始token用于记录
    // wechat: String,
    /// 是否在线，离线的玩家不允许提交分数，但保留在排行榜里。
    online: bool,
    /// 进入房间的顺序，用于游戏开始前展示前 20 个入房玩家。
    entry_order: usize,
    /// 玩家昵称
    nickname: String,
    /// 头像
    avatar: String,
    /// 是否曾经进入过历史前十榜，已上榜玩家本轮不再进入前十展示。
    already_top_ranked: bool,
}

#[derive(Clone)]
struct RankingPlayer {
    wechat: String,
    score: i64,
    nickname: String,
    avatar: String,
    already_top_ranked: bool,
}

#[derive(Clone, Copy, Default, PartialEq, Eq)]
enum RoomStatus {
    #[default]
    Closed = 0, //关闭
    Loading, //开始排队
    Running, //进行
    Submit,  //提交结果
    Ended,   //结束
}

#[derive(Default)]
struct RoomState {
    status: RoomStatus,
    players: HashMap<String, RoomPlayer>, // 玩家列表，key 是 userid
    next_entry_order: usize,
    current_game_id: Option<String>,
    current_item_num: i32,
    batch_no: String,
    round_started_at: Option<Instant>,
}

impl RoomState {
    pub fn get_player_list(&self) -> Value {
        json!({
            "player": vec![1],
            "count":0
        })
    }

    pub fn get_game_tilte(&self, key: Option<&str>) -> String {
        let key = if key.is_none() {
            &self.current_game_id
        } else {
            &Some("".to_string())
        };

        if let Some(key) = key {
            let mut GAME_MAP = HashMap::new();
            GAME_MAP.insert("game1", "击鼓齐福");
            GAME_MAP.insert("game2", "聚宝接福");
            GAME_MAP.insert("game3", "马上有福");
            GAME_MAP.insert("game4", "策马迎福");
            GAME_MAP.insert("game5", "圈彩纳福");
            GAME_MAP.insert("game6", "福运当头");
            let str = GAME_MAP.get(key.as_str()).map_or("", |v| v);
            str.to_string()
        } else {
            "".to_string()
        }
    }

    fn ranking_from_player(wechat: &str, player: &RoomPlayer) -> RankingPlayer {
        RankingPlayer {
            wechat: wechat.to_string(),
            score: player.score,
            nickname: player.nickname.clone(),
            avatar: player.avatar.clone(),
            already_top_ranked: player.already_top_ranked,
        }
    }

    fn sort_score_ranking(ranking: &mut Vec<RankingPlayer>) {
        ranking.sort_by(|a, b| b.score.cmp(&a.score).then_with(|| a.wechat.cmp(&b.wechat)));
    }

    fn ranking_payload(ranking: &[RankingPlayer]) -> Vec<Value> {
        ranking
            .iter()
            .enumerate()
            .map(|(index, player)| {
                json!({
                    "rank": index + 1,
                    // "wechat": player.wechat,
                    "score": player.score,
                    "nickname": player.nickname,
                    "avatar": player.avatar
                })
            })
            .collect()
    }
    // 房间开始进入排行
    fn entry_ranking(&self) -> Vec<RankingPlayer> {
        let mut ranking: Vec<(&String, &RoomPlayer)> = self.players.iter().collect();
        ranking.sort_by(|(_, a), (_, b)| b.entry_order.cmp(&a.entry_order));
        ranking
            .into_iter()
            .take(ROOM_ENTRY_LIMIT)
            .map(|(userid, player)| Self::ranking_from_player(userid, player))
            .collect()
    }
    // 游戏结束排行
    fn full_score_ranking(&self) -> Vec<RankingPlayer> {
        let mut ranking: Vec<RankingPlayer> = self
            .players
            .iter()
            .map(|(userid, player)| Self::ranking_from_player(userid, player))
            .collect();
        Self::sort_score_ranking(&mut ranking);
        ranking
    }

    fn eligible_score_ranking(&self) -> Vec<RankingPlayer> {
        let mut ranking: Vec<RankingPlayer> = self
            .players
            .iter()
            .filter(|(_, player)| !player.already_top_ranked)
            .map(|(userid, player)| Self::ranking_from_player(userid, player))
            .collect();
        Self::sort_score_ranking(&mut ranking);
        ranking
    }

    fn score_ranking(&self) -> Vec<RankingPlayer> {
        let mut ranking = self.eligible_score_ranking();
        ranking.truncate(ROOM_SCORE_RANK_LIMIT);
        ranking
    }

    fn score_result_payload(&self, wechat: &str) -> Option<Value> {
        let ranking = self.full_score_ranking();

        ranking
            .iter()
            .position(|player| player.wechat == wechat)
            .map(|index| json!(index + 1))
    }

    fn player_rank(&self, userid: &str) -> Option<usize> {
        self.eligible_score_ranking()
            .iter()
            .position(|player| player.wechat == userid)
            .map(|index| index + 1)
    }

    fn remaining_running_secs(&self) -> u64 {
        let Some(started_at) = self.round_started_at else {
            return GAME_RUNNING_SECS;
        };

        let elapsed = started_at.elapsed().as_secs();
        let play_elapsed = elapsed.saturating_sub(GAME_START_COUNTDOWN_SECS);
        GAME_RUNNING_SECS.saturating_sub(play_elapsed)
    }

    fn reconnect_payload(&self, userid: &str) -> Option<Value> {
        let player = self.players.get(userid)?;

        Some(json!({
            "reconnect": true,
            "status": self.status.as_str(),
            "gameId": self.current_game_id,
            "score": player.score,
            "rank": self.player_rank(userid),
            "flag": if player.already_top_ranked { 1 } else { 0 },
            "alreadyTopRanked": player.already_top_ranked,
            "remainingSeconds": self.remaining_running_secs(),
        }))
    }

    fn current_ranking_payload(&self) -> Vec<Value> {
        let current_ranking = if self.status == RoomStatus::Ended {
            self.score_ranking()
        } else {
            self.entry_ranking()
        };
        Self::ranking_payload(&current_ranking)
    }

    // fn snapshot(&self) -> Value {
    //     json!({
    //         "status": self.status.as_str(),
    //         "Loading": self.status == RoomStatus::Loading,
    //         "running": self.status == RoomStatus::Running,
    //         "ended": self.status == RoomStatus::Ended,
    //         "room_admin_count": get_admin_count(),
    //         "room_player_count": get_player_count(),
    //     })
    // }

    fn reset(&mut self) {
        self.status = RoomStatus::Closed;
        self.players.clear();
        self.next_entry_order = 0;
        self.current_game_id = None;
        self.current_item_num = 0;
        self.batch_no.clear();
        self.round_started_at = None;
    }
}

impl RoomStatus {
    fn as_str(self) -> &'static str {
        match self {
            RoomStatus::Closed => "closed",
            RoomStatus::Loading => "loading",
            RoomStatus::Running => "running",
            RoomStatus::Ended => "ended",
            RoomStatus::Submit => "submit",
        }
    }
}

// 获取玩家数量，不统计管理员
pub fn get_player_count() -> usize {
    CLIENT_PLAYER_MAP
        .iter()
        .filter(|player| player.value().0 > 0)
        .count()
}
// 获取管理员数量
pub fn get_admin_count() -> usize {
    CLIENT_ADMIN_SESSION
        .lock()
        .map(|session| usize::from(session.current.is_some()))
        .unwrap_or(0)
}

pub fn emit_to_user(userid: &str, event: &str, payload: &Value) {
    if userid.is_empty() {
        return;
    }

    if let Some(socket) = CLIENT_PLAYER_MAP.get(userid) {
        _ = socket.1.emit(event, payload);
    }
}

pub fn broadcast_clients(event: &str, payload: &Value) {
    for socket in CLIENT_PLAYER_MAP.iter() {
        _ = socket.value().1.emit(event, payload);
    }
}

fn valid_user(userid: &str, validuser: &str) -> bool {
    let aes_passphrase = utils::get_aes_passphrase();
    if aes_passphrase.is_none() {
        return true; // 没有配置 AES 密钥短语就不启用鉴权。
    }
    match utils::decrypt_cryptojs_aes(validuser, &aes_passphrase.unwrap_or_else(|| "".into())) {
        Ok(u) => &u == userid,
        Err(_) => false,
    }
}

fn parse_item_num(game_id: &str) -> i32 {
    game_id
        .trim()
        .strip_prefix("game")
        .unwrap_or_else(|| game_id.trim())
        .parse::<i32>()
        .unwrap_or(0)
}

fn decode_msgpack(data: &Data<Bytes>) -> Result<Value, rmp_serde::decode::Error> {
    rmp_serde::from_slice(data.0.as_ref())
}

fn encode_msgpack(value: &Value) -> Option<Bytes> {
    match rmp_serde::to_vec(value) {
        Ok(payload) => Some(Bytes::from(payload)),
        Err(_) => None,
    }
}

/// 回发给玩家
fn emit_msgpack(socket: &SocketRef, event: &str, payload: &Value) {
    if let Some(payload) = encode_msgpack(payload) {
        _ = socket.emit(event, &payload);
    }
}

/// 发送给所有在房间里游玩的玩家
fn broadcast_msgpack_all_player(event: &str, payload: &Value) {
    if let Some(payload) = encode_msgpack(payload) {
        for socket in CLIENT_PLAYER_MAP.iter() {
            let (state, socks) = socket.value();
            if *state > 0 {
                _ = socks.emit(event, &payload);
            }
        }
    }
}

/// 发送给所有玩家(不管是否在房间里)
fn broadcast_msgpack_all_player_2(event: &str, payload: &Value) {
    if let Some(payload) = encode_msgpack(payload) {
        for socket in CLIENT_PLAYER_MAP.iter() {
            let (state, socks) = socket.value();
            _ = socks.emit(event, &payload);
        }
    }
}
/// 发送给所有管理员
fn broadcast_msgpack_all_admin(event: &str, payload: &Value) {
    let Some(payload) = encode_msgpack(payload) else {
        return;
    };

    let socket = CLIENT_ADMIN_SESSION
        .lock()
        .ok()
        .and_then(|session| session.current.as_ref().map(|(_, socket)| socket.clone()));
    if let Some(socket) = socket {
        _ = socket.emit(event, &payload);
    }
}

fn replace_current_admin(userid: String, socket: SocketRef) -> Result<Option<SocketRef>, ()> {
    CLIENT_ADMIN_SESSION
        .lock()
        .map(|mut session| {
            let old_socket = session.current.take().map(|(_, socket)| socket);
            session.current = Some((userid, socket));
            session.version = session.version.wrapping_add(1);
            old_socket
        })
        .map_err(|_| ())
}

fn clear_current_admin_if_match(userid: &str, socket: &SocketRef) -> Option<u64> {
    CLIENT_ADMIN_SESSION
        .lock()
        .map(|mut session| {
            let should_clear = session
                .current
                .as_ref()
                .map(|(current_userid, current_socket)| {
                    current_userid == userid && current_socket.id == socket.id
                })
                .unwrap_or(false);
            if should_clear {
                session.current = None;
                session.version = session.version.wrapping_add(1);
                Some(session.version)
            } else {
                None
            }
        })
        .ok()
        .flatten()
}

fn admin_session_still_empty(version: u64) -> bool {
    CLIENT_ADMIN_SESSION
        .lock()
        .map(|session| session.current.is_none() && session.version == version)
        .unwrap_or(false)
}

fn spawn_deactivate_players_if_admin_absent(version: u64) {
    tokio::spawn(async move {
        tokio::time::sleep(Duration::from_secs(ADMIN_ABSENT_DEACTIVATE_PLAYER_SECS)).await;

        if !admin_session_still_empty(version) {
            return;
        }

        for mut player in CLIENT_PLAYER_MAP.iter_mut() {
            player.value_mut().0 = 0;
        }
        CLIENT_TOP_RANKED_MAP.clear();
    });
}
/// 发送给所有管理员和玩家
fn broadcast_msgpack_all(event: &str, payload: &Value) {
    broadcast_msgpack_all_admin(event, payload);
    broadcast_msgpack_all_player(event, payload);
}

fn ok_resp(data: Option<Value>) -> Value {
    json!({
        "state": 1,
        "msg": "",
        "data": data,
    })
}

fn err_resp(msg: &str, data: Option<Value>) -> Value {
    json!({
        "state": 0,
        "msg": msg,
        "data": data,
    })
}

fn broadcast_room_rank(ranking: Vec<Value>) {
    broadcast_msgpack_all_admin("room_rank_result", &ok_resp(Some(json!(ranking))));
}

fn emit_room_rank(socket: &SocketRef, ranking: Vec<Value>) {
    emit_msgpack(socket, "room_rank_result", &ok_resp(Some(json!(ranking))));
}

fn broadcast_room_rank_to_spectators(ranking: &[Value], current_player_ids: &HashSet<String>) {
    let payload = ok_resp(Some(json!(ranking)));
    let Some(payload) = encode_msgpack(&payload) else {
        return;
    };

    for player in CLIENT_PLAYER_MAP.iter() {
        let userid = player.key();
        let (state, socket) = player.value();
        if *state == 0 && !current_player_ids.contains(userid) {
            _ = socket.emit("room_rank_result", &payload);
        }
    }
}

fn emit_msgpack_to_user(userid: &str, event: &str, payload: &Value) {
    if userid.is_empty() {
        return;
    }

    if let Some(socket) = CLIENT_PLAYER_MAP.get(userid) {
        emit_msgpack(&socket.1, event, payload);
    }
}

fn is_current_player_socket(userid: &str, socket: &SocketRef) -> bool {
    CLIENT_PLAYER_MAP
        .get(userid)
        .map(|player| player.1.id == socket.id)
        .unwrap_or(false)
}

fn push_room_score_ranking() -> bool {
    let (ranking, player_results, current_player_ids) = if let Ok(state) = ROOM_STATE.lock() {
        let full_ranking = state.full_score_ranking();
        let eligible_ranking = state.eligible_score_ranking();
        let remaining_seconds = state.remaining_running_secs();
        let ranking = RoomState::ranking_payload(
            &eligible_ranking
                .iter()
                .take(ROOM_SCORE_RANK_LIMIT)
                .cloned()
                .collect::<Vec<_>>(),
        );
        let eligible_rank_map = eligible_ranking
            .iter()
            .enumerate()
            .map(|(index, player)| (player.wechat.clone(), index + 1))
            .collect::<HashMap<_, _>>();
        let player_results = full_ranking
            .into_iter()
            .map(|player| {
                let rank = eligible_rank_map.get(&player.wechat).cloned();
                let mut result = json!({
                    "score": player.score,
                    "rank": rank,
                    "remainingSeconds": remaining_seconds
                });
                if player.already_top_ranked {
                    result["alreadyTopRanked"] = json!(true);
                    result["message"] =
                        json!("一人只能上榜领奖一次，您的福利已到手，把机会留给其他玩家吧");
                }
                (player.wechat, result)
            })
            .collect::<Vec<_>>();
        let current_player_ids = state.players.keys().cloned().collect::<HashSet<_>>();

        (ranking, player_results, current_player_ids)
    } else {
        return false;
    };

    broadcast_room_rank(ranking.clone());
    broadcast_room_rank_to_spectators(&ranking, &current_player_ids);
    for (userid, result) in player_results {
        emit_msgpack_to_user(&userid, "submit_score_result", &ok_resp(Some(result)));
    }

    true
}

fn game_code(item_num: i32) -> &'static str {
    match item_num {
        1 => "JGQF",
        2 => "JBJF",
        3 => "MSYF",
        4 => "CMYF",
        5 => "QCNF",
        6 => "FYDT",
        _ => "",
    }
}

async fn post_screen_game_score(game: &Game) {
    let auth_token = CLIENT_AUTH_TOKEN_MAP
        .get(&game.wechat_id)
        .map(|token| token.value().clone())
        .unwrap_or_default();

    if auth_token.is_empty() {
        println!(
            "[screen_game_score] skip: empty auth token, wechat_id={}, item_num={}, score={}, rank={}, batch_no={}",
            game.wechat_id, game.item_num, game.score, game.game_rank, game.batch_no
        );
        return;
    }

    let code = game_code(game.item_num);
    if code.is_empty() {
        println!(
            "[screen_game_score] skip: empty game code, wechat_id={}, item_num={}, score={}, rank={}, batch_no={}",
            game.wechat_id, game.item_num, game.score, game.game_rank, game.batch_no
        );
        return;
    }

    let body = json!({
        "game_code": code,
        "score": game.score,
        "batch_no": game.batch_no,
        "rank_no": game.game_rank,
    });
    dbg!(json!({
        "event": "screen_game_score_post",
        "wechat_id": game.wechat_id.clone(),
        "auth_token_len": auth_token.len(),
        "body": body.clone(),
    }));

    let resp = reqwest::Client::new()
        .post("https://api.guocai365.org.cn/api/offline_clearance/user/screen_game_score")
        .header("Authorization", auth_token)
        .json(&body)
        .send()
        .await;

    let log_msg = match resp {
        Ok(resp) if resp.status().is_success() => {
            let status = resp.status();
            let msg = resp.text().await.unwrap_or_default();
            dbg!(
                "[screen_game_score] success: wechat_id={}, status={}, msg={}",
                &game.wechat_id,
                status,
                msg
            );
            return;
        }
        Ok(resp) => {
            let status = resp.status();
            let body = resp.text().await.unwrap_or_default();
            dbg!(json!({
                "event": "screen_game_score_fail",
                "wechat_id": game.wechat_id.clone(),
                "status": status.as_u16(),
                "body": body.clone(),
            }));
            format!("提交第三方大屏游戏分数失败: {status}, {body}")
        }
        Err(err) => {
            dbg!(json!({
                "event": "screen_game_score_error",
                "wechat_id": game.wechat_id.clone(),
                "err": err.to_string(),
            }));
            err.to_string()
        }
    };

    {
        let log = Log {
            id: 0,
            msg: log_msg,
            params: json!({
                "wechat_id": game.wechat_id,
                "item_num": game.item_num,
                "game_rank": game.game_rank,
                "score": game.score,
                "batch_no": game.batch_no,
            }),
            create_date: None,
        };
        log.insert().await;
    }
}

async fn insert_game_once(game: &Game) -> bool {
    let save_key = format!("{}:{}:{}", game.batch_no, game.item_num, game.wechat_id);
    match SAVED_GAME_RECORD_MAP.entry(save_key.clone()) {
        Entry::Occupied(_) => return true,
        Entry::Vacant(entry) => {
            entry.insert(true);
        }
    }

    let inserted = game.insert_once().await;
    if !inserted {
        SAVED_GAME_RECORD_MAP.remove(&save_key);
    } else {
        post_screen_game_score(game).await;
    }

    inserted
}

async fn save_room_final_scores() {
    let games = if let Ok(state) = ROOM_STATE.lock() {
        if state.batch_no.is_empty() || state.current_item_num <= 0 {
            return;
        }

        let batch_no = state.batch_no.clone();
        let item_num = state.current_item_num;
        let full_ranking = state.full_score_ranking();
        let eligible_ranking = state.eligible_score_ranking();
        let eligible_rank_map = eligible_ranking
            .iter()
            .enumerate()
            .map(|(index, player)| (player.wechat.clone(), (index + 1) as i32))
            .collect::<HashMap<_, _>>();

        full_ranking
            .into_iter()
            .map(|player| {
                let game_rank = if player.already_top_ranked {
                    0
                } else {
                    eligible_rank_map.get(&player.wechat).cloned().unwrap_or(0)
                };

                Game {
                    wechat_id: player.wechat,
                    nickname: player.nickname,
                    avatar: player.avatar,
                    item_num,
                    game_rank,
                    score: player.score as i32,
                    create_date: None,
                    batch_no: batch_no.clone(),
                }
            })
            .collect::<Vec<_>>()
    } else {
        return;
    };

    for game in games {
        dbg!(json!({
            "event": "final_score_save",
            "wechat_id": game.wechat_id.clone(),
            "item_num": game.item_num,
            "score": game.score,
            "rank": game.game_rank,
            "batch_no": game.batch_no.clone(),
        }));
        insert_game_once(&game).await;
    }
}

async fn sleep_room_status(round_id: u64, secs: u64, expected_status: RoomStatus) -> bool {
    for _ in 0..secs {
        tokio::time::sleep(Duration::from_secs(1)).await;

        if GAME_ROUND_ID.load(Ordering::SeqCst) != round_id {
            return false;
        }

        if let Ok(state) = ROOM_STATE.lock() {
            if state.status != expected_status {
                return false;
            }
        }
    }

    true
}

fn spawn_game_timeout(round_id: u64, secs: u64) {
    tokio::spawn(async move {
        let start_countdown_secs = GAME_START_COUNTDOWN_SECS;
        let result_wait_secs = GAME_RESULT_WAIT_SECS;
        let running_secs = secs.saturating_sub(start_countdown_secs + result_wait_secs);

        if !sleep_room_status(round_id, start_countdown_secs, RoomStatus::Running).await {
            return;
        }

        for _ in 0..running_secs {
            tokio::time::sleep(Duration::from_secs(1)).await;

            if GAME_ROUND_ID.load(Ordering::SeqCst) != round_id {
                return;
            }

            if let Ok(state) = ROOM_STATE.lock() {
                if state.status != RoomStatus::Running {
                    return;
                }
            }

            // 游戏正式计时期间，每秒统一给大屏和玩家推一次实时排名。
            if !push_room_score_ranking() {
                return;
            }
        }

        if let Ok(mut state) = ROOM_STATE.lock() {
            if state.status == RoomStatus::Running {
                state.status = RoomStatus::Submit;
            } else {
                return;
            }
        }

        if !sleep_room_status(round_id, result_wait_secs, RoomStatus::Submit).await {
            return;
        }

        save_room_final_scores().await;

        if let Ok(mut state) = ROOM_STATE.lock() {
            if state.status == RoomStatus::Submit {
                state.status = RoomStatus::Ended;
            }
        }
    });
}

// fn spawn_game_timeout(round_id: u64, secs: u64) {
//     tokio::spawn(async move {
//         tokio::time::sleep(Duration::from_secs(secs)).await;

//         if GAME_ROUND_ID.load(Ordering::SeqCst) != round_id {
//             return;
//         }

//         if let Ok(mut state) = ROOM_STATE.lock() {
//             state.status = RoomStatus::Submit;
//             // dbg!("submit");
//             drop(state);
//         }
//     });
// }

// fn current_room_snapshot() -> Value {
//     if let Ok(state) = ROOM_STATE.lock() {
//         let snapshot = state.snapshot();
//         drop(state);
//         snapshot
//     } else {
//         json!({})
//     }
// }

async fn handle_room_command(
    socket: &SocketRef,
    userid: &str,
    is_admin: bool,
    cmd: &str,
    data: &Value,
) {
    let evt = &format!("{cmd}_result");
    match cmd {
        //回到首页时
        "home" => {
            if !is_admin {
                return;
            }
            GAME_ROUND_ID.fetch_add(1, Ordering::SeqCst);
            if let Ok(mut state) = ROOM_STATE.lock() {
                state.reset();
            }
        }
        "room_create" | "room_back" => {
            //room_back返回当前游戏的Loading状态
            // 只有管理员可以创建房间
            if !is_admin {
                return;
            }
            if let Some(index) = data.as_i64() {
                if let Ok(mut state) = ROOM_STATE.lock() {
                    let game_id = format!("game{index}");
                    if cmd == "room_create" {
                        if state.status != RoomStatus::Closed {
                            let title = state.get_game_tilte(Some(&game_id));
                            drop(state);
                            emit_msgpack(
                                &socket,
                                evt,
                                &err_resp(&format!("不能重复创建游戏 - {} 房间", title), None),
                            );
                            return;
                        }
                    }
                    state.reset();
                    state.current_game_id = Some(game_id);
                    state.current_item_num = index as i32;
                    state.status = RoomStatus::Loading;
                    // let snapshot = state.snapshot();
                    drop(state);
                    // dbg!(&CLIENT_PLAYER_MAP.len());
                    emit_msgpack(&socket, evt, &ok_resp(Some(json!(index))));
                    broadcast_msgpack_all_player_2(evt, &ok_resp(Some(json!(index))));
                }
            }
        }
        "room_join" => {
            // 玩家才能进入
            if is_admin {
                return;
            }
            // dbg!(&data);
            //超过300人的情况
            if get_player_count() > 300 {
                emit_msgpack(&socket, evt, &err_resp("本场游戏人员已满", Some(json!(3))));
                return;
            }
            let gameId = data
                .get("gameId")
                .and_then(|v| v.as_str())
                .unwrap_or("")
                .to_string();
            let nickname = data
                .get("nickname")
                .and_then(|value| value.as_str())
                .unwrap_or("")
                .to_string();
            let avatar = data
                .get("avatar")
                .and_then(|value| value.as_str())
                .unwrap_or("")
                .to_string();
            let already_top_ranked = Game::get_rank(userid, parse_item_num(&gameId)).await > 0;
            CLIENT_TOP_RANKED_MAP.insert(userid.to_string(), already_top_ranked);

            if let Ok(mut state) = ROOM_STATE.lock() {
                // 当前h5进入的不是大屏开启的游戏房间
                if gameId != state.current_game_id.clone().unwrap_or_default() {
                    drop(state);
                    emit_msgpack(
                        &socket,
                        evt,
                        &err_resp(&format!("当前游戏房间还未开启"), None),
                    );
                    return;
                }

                if matches!(state.status, RoomStatus::Closed) {
                    return;
                } else if matches!(state.status, RoomStatus::Running) {
                    //如果是正在游戏中
                    let already_joined = state.players.contains_key(userid);
                    //玩家游戏过程中掉线重连
                    if already_joined {
                        if let Some(player) = state.players.get_mut(userid) {
                            player.online = true;
                            player.nickname = nickname.to_string();
                            player.avatar = avatar.to_string();
                            player.already_top_ranked = already_top_ranked;
                        }
                        let reconnect_payload = state.reconnect_payload(userid);
                        drop(state);
                        if let Some(mut player) = CLIENT_PLAYER_MAP.get_mut(userid) {
                            player.0 = 1;
                        }
                        emit_msgpack(&socket, "room_recover_result", &ok_resp(reconnect_payload));
                        return;
                    } else {
                        let ranking = RoomState::ranking_payload(&state.score_ranking());
                        drop(state);
                        emit_room_rank(socket, ranking);
                        return;
                    }
                } else if matches!(state.status, RoomStatus::Submit | RoomStatus::Ended) {
                    let ranking = RoomState::ranking_payload(&state.score_ranking());
                    drop(state);
                    emit_room_rank(socket, ranking);
                    return;
                }
                let entry_order = state.next_entry_order;
                state.next_entry_order += 1;

                state
                    .players
                    .entry(userid.to_string())
                    .and_modify(|player| {
                        player.online = true;
                        player.nickname = nickname.to_string();
                        player.avatar = avatar.to_string();
                        player.already_top_ranked = already_top_ranked;
                    })
                    .or_insert(RoomPlayer {
                        score: 0,
                        online: true,
                        entry_order,
                        nickname: nickname.to_string(),
                        avatar: avatar.to_string(),
                        already_top_ranked,
                    });
                let count = state.players.len();
                let ranking = state.current_ranking_payload();
                // let snapshot = state.snapshot();
                drop(state);

                if let Some(mut player) = CLIENT_PLAYER_MAP.get_mut(userid) {
                    player.0 = 1;
                }
                emit_msgpack(
                    &socket,
                    evt,
                    &ok_resp(Some(json!({
                        "gameId":gameId,//当前游戏
                        "alreadyTopRanked": already_top_ranked,//是否上过前十榜
                    }))),
                );
                // 给所有管理员广播房间状态更新
                broadcast_msgpack_all_admin(
                    evt,
                    &ok_resp(Some(json!({"list":ranking,"count":count}))),
                );
            }
        }
        "game_start" => {
            if !is_admin {
                return;
            }
            let game_index = data.as_str().unwrap_or("");
            let start_info = if let Ok(mut state) = ROOM_STATE.lock() {
                if state.players.is_empty() {
                    drop(state);
                    emit_msgpack(&socket, evt, &err_resp("房间中没有玩家", Some(json!(1))));
                    return;
                }
                if state.status != RoomStatus::Loading {
                    drop(state);
                    emit_msgpack(&socket, evt, &err_resp("房间没有创建", Some(json!(2))));
                    return;
                }

                state.status = RoomStatus::Running;
                state.current_game_id = Some(game_index.to_string());
                if state.current_item_num <= 0 {
                    state.current_item_num = parse_item_num(game_index);
                }
                state.round_started_at = Some(Instant::now());
                let round_id = GAME_ROUND_ID.fetch_add(1, Ordering::SeqCst) + 1;
                // let snapshot = state.snapshot();
                let play_len = state.players.len();
                let item_num = state.current_item_num;
                //每次开始房间时记录当前批次
                let batch_no = format!("{}-{}", utils::get_current_timestamp(), play_len);
                state.batch_no = batch_no.clone();
                SAVED_GAME_RECORD_MAP.clear();
                Some((round_id, batch_no, item_num, play_len))

                //通知所有玩家倒计时60秒,实际上还有3秒倒计时,以前2秒统计结果,所以是65
            } else {
                None
            };
            let Some((round_id, batch_no, item_num, play_len)) = start_info else {
                return;
            };

            {
                let room = GameRoom {
                    batch_no: batch_no.clone(),
                    item_num,
                    player_count: play_len as i32,
                    create_date: None,
                };
                if !room.insert().await {
                    if let Ok(mut state) = ROOM_STATE.lock() {
                        if state.batch_no == batch_no {
                            state.status = RoomStatus::Loading;
                            state.batch_no.clear();
                            state.round_started_at = None;
                        }
                    }
                    emit_msgpack(&socket, evt, &err_resp("房间批次保存失败", None));
                    return;
                }

                spawn_game_timeout(
                    round_id,
                    GAME_START_COUNTDOWN_SECS + GAME_RUNNING_SECS + GAME_RESULT_WAIT_SECS,
                );
                //utils::get_current_timestamp()
                broadcast_msgpack_all_player(
                    evt,
                    &ok_resp(Some(json!({
                        "index":game_index,
                        "batch_no":batch_no
                    }))),
                );
                //通知管理员
                emit_msgpack(&socket, evt, &ok_resp(Some(json!(game_index))));
            }
        }
        // submit_score玩家提交分数
        // submit_score_save则为60秒后提交的数据
        "submit_score" => {
            if is_admin {
                return;
            }

            let score = data.get("score").and_then(|v| v.as_i64()).unwrap_or(0);
            if let Ok(mut state) = ROOM_STATE.lock() {
                if !state.players.contains_key(userid) {
                    drop(state);
                    emit_msgpack(&socket, evt, &err_resp("您不在房间中", None));
                    return;
                } else if !matches!(state.status, RoomStatus::Running | RoomStatus::Submit) {
                    // dbg!(&state.status.clone().as_str());
                    // let title = state.get_game_tilte(None);
                    let status = state.status.clone().as_str();
                    drop(state);
                    emit_msgpack(
                        &socket,
                        evt,
                        &err_resp(&format!("当前游戏状态{status}不能提交分数"), None),
                    );
                    return;
                } else if state.players.contains_key(userid) {
                    let player = state.players.get_mut(userid).unwrap();
                    player.score = score;
                    // player.wechat = wechat.to_string();
                    player.online = true;
                    let score = player.score;
                    let already_top_ranked = player.already_top_ranked;
                    let rank = if already_top_ranked {
                        None
                    } else {
                        state.player_rank(userid)
                    };
                    let remaining_seconds = state.remaining_running_secs();
                    drop(state);
                    let mut result = json!({
                        "score": score,
                        "rank": rank,
                        "remainingSeconds": remaining_seconds
                    });
                    if already_top_ranked {
                        result["alreadyTopRanked"] = json!(true);
                        result["message"] =
                            json!("一人只能上榜领奖一次，您的福利已到手，把机会留给其他玩家吧");
                    }
                    emit_msgpack(&socket, evt, &ok_resp(Some(result)));
                    return;

                    // 方案1:每有一名玩家提交时发送给所有玩家跟管理员,玩家过多时发送太频繁,
                    // 这里会选第2种方案, 游戏中时每秒发给所有玩家跟管理员 (push_room_score_ranking)
                    // 玩家分数排序
                    // let ranking = RoomState::ranking_payload(&state.score_ranking());
                    // let player_rank = state.score_result_payload(userid);
                    // drop(state);
                    // emit_msgpack(&socket, evt, &ok_resp(player_rank));
                    // broadcast_room_rank(ranking);
                } else {
                    drop(state);
                    emit_msgpack(&socket, evt, &err_resp("提交异常", None));
                    return;
                }
            }
        }
        // 将数据提交到数据库,不用通知管理员
        "submit_score_save" => {
            let score = data.get("score").and_then(|v| v.as_i64()).unwrap_or(0);
            if let (Some(item_num), Some(wechat), Some(nickname), Some(avatar), Some(rank)) = (
                data.get("item_num").and_then(|v| v.as_i64()),
                data.get("wechat").and_then(|v| v.as_str()), // 需要原始token
                data.get("nickname").and_then(|v| v.as_str()),
                data.get("avatar").and_then(|v| v.as_str()),
                data.get("rank").and_then(|v| v.as_i64()),
            ) {
                if wechat.is_empty() {
                    return;
                }
                let (rank, ranking, already_top_ranked) =
                    if let Ok(state) = ROOM_STATE.lock() {
                        let mut state = state;
                        //防止客户端提交的状态还在Running中
                        if !matches!(state.status, RoomStatus::Submit | RoomStatus::Running) {
                            drop(state);
                            return;
                        }
                        if let Some(player) = state.players.get_mut(userid) {
                            player.score = score;
                            player.nickname = nickname.to_string();
                            player.avatar = avatar.to_string();
                            player.online = true;
                        }
                        let eligible_ranking = state.eligible_score_ranking();
                        let ranking = RoomState::ranking_payload(
                            &eligible_ranking
                                .iter()
                                .take(ROOM_SCORE_RANK_LIMIT)
                                .cloned()
                                .collect::<Vec<_>>(),
                        );
                        let already_top_ranked = state
                            .players
                            .get(userid)
                            .map(|player| player.already_top_ranked)
                            .unwrap_or(false);
                        let server_rank = if already_top_ranked {
                            0
                        } else {
                            eligible_ranking
                                .iter()
                                .position(|player| player.wechat == userid)
                                .map(|index| (index + 1) as i64)
                                .unwrap_or(rank)
                        };
                        drop(state);
                        (
                            server_rank,
                            ranking,
                            already_top_ranked,
                        )
                    } else {
                        (rank, Vec::new(), false)
                    };
                dbg!(json!({
                    "event": "submit_score_save_deferred",
                    "wechat_id": wechat,
                    "item_num": item_num,
                    "score": score,
                    "rank": rank,
                    "already_top_ranked": already_top_ranked,
                }));
                let mut payload = json!({
                    "list": ranking,
                    "rank": if already_top_ranked { Value::Null } else { json!(rank) }
                });
                if already_top_ranked {
                    payload["alreadyTopRanked"] = json!(true);
                    payload["message"] =
                        json!("一人只能上榜领奖一次，您的福利已到手，把机会留给其他玩家吧");
                }
                emit_msgpack(&socket, evt, &ok_resp(Some(payload)));
            }
        }
        // 关闭房间
        "room_close" => {
            if !is_admin {
                return;
            }

            if let Ok(mut state) = ROOM_STATE.lock() {
                state.reset();
            }

            let player_sockets = CLIENT_PLAYER_MAP
                .iter()
                .map(|player| player.value().1.clone())
                .collect::<Vec<_>>();
            CLIENT_PLAYER_MAP.clear();
            CLIENT_TOP_RANKED_MAP.clear();
            CLIENT_AUTH_TOKEN_MAP.clear();
            SAVED_GAME_RECORD_MAP.clear();

            emit_msgpack(&socket, evt, &ok_resp(None));
            // broadcast_msgpack_all_player_2(evt, &ok_resp(None));
            for player_socket in player_sockets {
                _ = player_socket.disconnect();
            }
        }
        // 获取房间状态
        "room_state" | "status" => {
            if let Ok(state) = ROOM_STATE.lock() {
                let gameId = state.current_game_id.clone().unwrap_or_default();
                let count = state.players.len();
                let ranking = state.current_ranking_payload();
                let status = state.status as i32;
                let player_rank = if is_admin {
                    Value::Null
                } else {
                    state.player_rank(userid).map_or(Value::Null, |rank| json!(rank))
                };
                let player_score = if is_admin {
                    Value::Null
                } else {
                    state
                        .players
                        .get(userid)
                        .map_or(Value::Null, |player| json!(player.score))
                };
                // let snapshot = state.snapshot();
                drop(state);
                emit_msgpack(
                    &socket,
                    evt,
                    &ok_resp(Some(
                        json!({"status":status,"list":ranking,"count":count,"gameId":gameId,"rank":player_rank,"score":player_score,}),
                    )),
                );
            }
        }
        // 房间排序
        "room_rank" => {
            if let Ok(state) = ROOM_STATE.lock() {
                let ranking = state.current_ranking_payload();
                drop(state);
                emit_msgpack(&socket, evt, &ok_resp(Some(json!(ranking))));
            } else {
                emit_msgpack(
                    &socket,
                    evt,
                    &err_resp("too many requests, please try again later", None),
                );
            }
        }
        _ => {
            //emit_msgpack(&socket, "unknow_cmd", &&err_resp("未知指令", None));
        }
    };
}

pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value>) {
    let userid = data
        .get("userid")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .to_string();

    // 鉴权失败直接断开连接，鉴权通过后才会在内存里记录这个连接。
    if userid.is_empty() {
        _ = socket.disconnect();
        return;
    }

    if let Some(token) = data.get("token").and_then(|v| v.as_str()).filter(|v| !v.is_empty()) {
        println!(
            "[screen_game_score] cache auth token: userid={}, token_len={}",
            userid,
            token.len()
        );
        CLIENT_AUTH_TOKEN_MAP.insert(userid.clone(), token.to_string());
    }

    let mut is_admin = false;

    // 接入微信小程序不做验证，但只要传了 validuser 字段且验证成功，就是管理员。
    let validuser = data.get("validuser").and_then(|v| v.as_str()).unwrap_or("");
    if !validuser.is_empty() {
        if valid_user(&userid, validuser) {
            // dbg!(&userid);
            match Manager::Login(ManagerLogin::token(userid.clone())).await {
                Ok(_res) => {
                    is_admin = true;
                }
                Err(err) => {
                    // dbg!(&err);
                    //可能token更改了，直接断开连接让管理员重新登录获取新token
                    emit_msgpack(
                        &socket,
                        "login_result",
                        &err_resp("登录信息已过期", Some(json!(2))),
                    );
                    _ = socket.disconnect();
                    return;
                }
            }
        } else {
            // 传了 validuser 字段但验证失败，直接断开连接。
            _ = socket.disconnect();
            return;
        }
    }

    if is_admin {
        match replace_current_admin(userid.clone(), socket.clone()) {
            Ok(Some(old_socket)) => {
                emit_msgpack(
                    &old_socket,
                    "new_admin_result",
                    &err_resp("有其他管理员登录,您已下线", None),
                );
                _ = old_socket.disconnect();
            }
            Ok(None) => {}
            Err(_) => {
                _ = socket.disconnect();
                return;
            }
        }
    } else {
        CLIENT_TOP_RANKED_MAP.insert(userid.clone(), false);

        if get_admin_count() == 0 {
            match CLIENT_PLAYER_MAP.entry(userid.clone()) {
                Entry::Occupied(mut entry) => {
                    entry.insert((0, socket.clone()));
                }
                Entry::Vacant(entry) => {
                    entry.insert((0, socket.clone()));
                }
            }
            emit_msgpack(
                &socket,
                "connect_result",
                &err_resp("游戏管理员暂未开启该房间", None),
            );
            return;
        }
        match CLIENT_PLAYER_MAP.entry(userid.clone()) {
            Entry::Occupied(mut entry) => {
                // 同一玩家重连时用新 socket 接管，旧 socket 后续消息会被忽略。
                entry.insert((0, socket.clone()));
            }
            Entry::Vacant(entry) => {
                entry.insert((0, socket.clone()));
            }
        }
    }
    // 鉴权通过后，在这里挂玩家消息事件处理器。
    let message_userid = userid.clone();
    let message_is_admin = is_admin;
    socket.on("message", move |socket: SocketRef, data: Data<Bytes>| {
        let message_userid = message_userid.clone();
        async move {
            let data = match decode_msgpack(&data) {
                Ok(data) => data,
                Err(_err) => {
                    //dbg!("invalid_message", socket.id, err.to_string());
                    return;
                }
            };
            if let (Some(cmd), Some(data)) =
                (data.get("cmd").and_then(|v| v.as_str()), data.get("data"))
            {
                if !message_is_admin && !is_current_player_socket(&message_userid, &socket) {
                    return;
                }
                handle_room_command(&socket, &message_userid, message_is_admin, cmd, &data).await;
            }
        }
    });

    socket.on_disconnect({
        move |socket: SocketRef, _reason: DisconnectReason| async move {
            if is_admin {
                let Some(admin_session_version) = clear_current_admin_if_match(&userid, &socket)
                else {
                    return;
                };
                // 前端页面刷新时房间状态丢失体验会不好
                if get_admin_count() == 0 {
                    if let Ok(mut state) = ROOM_STATE.lock() {
                        state.reset(); // 如果管理员都退出了，就把房间状态重置。
                        drop(state);
                    }
                    spawn_deactivate_players_if_admin_absent(admin_session_version);
                }
            } else {
                if !CLIENT_PLAYER_MAP
                    .get(&userid)
                    .map(|player| player.1.id == socket.id)
                    .unwrap_or(false)
                {
                    return;
                }

                if let Ok(mut state) = ROOM_STATE.lock() {
                    match state.status {
                        RoomStatus::Closed => {
                            CLIENT_PLAYER_MAP.remove(&userid);
                            CLIENT_TOP_RANKED_MAP.remove(&userid);
                            state.players.remove(&userid);
                        }
                        RoomStatus::Loading => {
                            CLIENT_PLAYER_MAP.remove(&userid);
                            CLIENT_TOP_RANKED_MAP.remove(&userid);
                            if state.players.remove(&userid).is_none() {
                                return;
                            }
                            let count = state.players.len();
                            let ranking = state.current_ranking_payload();
                            drop(state);
                            broadcast_msgpack_all_admin(
                                "room_join_result",
                                &ok_resp(Some(json!({"list":ranking,"count":count}))),
                            );
                        }
                        RoomStatus::Running | RoomStatus::Submit | RoomStatus::Ended => {
                            let Some(player) = state.players.get_mut(&userid) else {
                                return;
                            };
                            player.online = false;
                            if let Some(mut player) = CLIENT_PLAYER_MAP.get_mut(&userid) {
                                player.0 = 0;
                            }
                            drop(state);
                            // 暂不发送
                            // broadcast_msgpack_all_admin("player_offline_result", &ok_resp(None));
                        }
                    }
                }
            }
        }
    });
}

#[handler]
pub async fn getTop10Rank(req: &mut Request, res: &mut Response) {
    let params = match req.parse_body::<HashMap<String, String>>().await {
        Ok(params) => params,
        Err(_) => {
            res.render(Json(err_resp("参数格式错误", None)));
            return;
        }
    };

    let batch_no = params
        .get("batch_no")
        .map(|value| value.trim())
        .unwrap_or_default();

    match Game::get_top10(batch_no).await {
        Ok(list) => {
            let payload = list
                .iter()
                .map(|game| {
                    json!({
                        "wechat_id": game.wechat_id,
                        "nickname": game.nickname,
                        "avatar": game.avatar,
                        "item_num": game.item_num,
                        "game_rank": game.game_rank,
                        "score": game.score,
                        "batch_no": game.batch_no,
                    })
                })
                .collect::<Vec<_>>();
            res.render(Json(ok_resp(Some(json!(payload)))));
        }
        Err(err) => {
            res.render(Json(err_resp(&err, None)));
        }
    }
}

#[handler]
pub async fn getConn(req: &mut Request, res: &mut Response) {
    res.render(Json(ok_resp(Some(json!({
        "socket_path": &config.socket_path.clone(),
        "client_ns":&&config.client_ns.clone()
    })))));
}

#[handler]
pub async fn login(req: &mut Request, res: &mut Response) {
    let params = match req.parse_body::<HashMap<String, String>>().await {
        Ok(params) => params,
        Err(_) => {
            res.render(Json(err_resp("参数格式错误", None)));
            return;
        }
    };

    let username = params
        .get("username")
        .map(|value| value.trim())
        .unwrap_or_default();
    let password = params
        .get("password")
        .map(|value| value.trim())
        .unwrap_or_default();

    if username.is_empty() || password.is_empty() {
        res.render(Json(err_resp("username 和 password 不能为空", None)));
        return;
    }

    let manager = match Manager::Login(ManagerLogin::user(
        username.to_string(),
        password.to_string(),
    ))
    .await
    {
        Ok(manager) => manager,
        Err(err) => {
            res.status_code(StatusCode::UNAUTHORIZED);
            res.render(Json(err_resp(&err, None)));
            return;
        }
    };

    let passphrase = utils::get_aes_passphrase();
    if passphrase.is_none() {
        res.render(Json(err_resp("用户名或密码不能为空", None)));
        return;
    }
    if get_admin_count() > 0 {
        if let Ok(state) = ROOM_STATE.lock() {
            if matches!(
                state.status,
                RoomStatus::Loading | RoomStatus::Running | RoomStatus::Submit
            ) {
                let title = state.get_game_tilte(None);
                drop(state);
                res.render(Json(err_resp(
                    &format!("当前管理员已开启游戏房间 - {},请稍后再登录", title),
                    None,
                )));
                return;
            }
        }
    }
    res.render(Json(ok_resp(Some(json!({
        "passphrase": passphrase,
        "token": manager.token,
        "socket_path": &config.socket_path.clone(),
        "client_ns":&&config.client_ns.clone()
    })))));
}
