use std::{
    collections::BTreeMap,
    rc::{Rc, Weak},
};

use common::tools::time_utils;
use proto::{
    OpenStateUpdateNotify, PlayerBasicCompBin, PlayerGameTimeNotify, Retcode, ServerTimeNotify,
};
use tokio::sync::OnceCell;

use crate::player::Player;

#[derive(Default)]
pub struct PlayerBasicComp {
    player: OnceCell<Weak<Player>>,
    is_guid_init: bool,
    last_world_level_limit: u32,
    pub level: u32,
    pub exp: u32,
    pub nickname: String,
    pub total_game_time: u32,
    pub trans_no_count: u64,
    pub is_weather_locked: bool,
    pub is_game_time_locked: bool,
    pub persist_stamina_limit: f32,
    pub cur_persist_stamina: f32,
    pub cur_temporary_stamina: f32,
    pub open_state_map: BTreeMap<u32, u32>,
    pub player_time_ms: u64,
    pub last_login_time: u32,
    pub register_time: u32,
    pub total_login_days: u32,
    pub update_login_days_time: u32,
    pub reward_taken_level_list: Vec<u32>,
    pub language_type: u32,
    pub client_app_version: String,
    pub client_device_info: String,
    pub client_system_version: String,
    pub is_guest: bool,
    pub online_time: u32,
    pub is_proficient_player: bool,
    pub set_language_tag: u32,
    pub head_image_avatar_id: u32,
    pub guid_seq_id: u32,
    pub is_rebate_mail_sent: bool,
    pub is_rebate_mail_received: bool,
    pub register_cps: String,
    pub cur_json_climate_type: u32,
    pub cur_climate_meter: f32,
    pub last_logout_time: u32,
    pub data_version: u32,
    pub last_daily_refresh_time: u32,
    pub is_auto_unlock_specific_equip: bool,
    pub profile_picture_costume_id: u32,
    pub online_id: String,
    pub psn_id: String,
    pub last_login_platform: u32,
    pub pivot_client_time: f64,
    pub pivot_unix_time: f64,
    pub client_total_tick_time: f64,
    pub player_stat_id: u64,
    pub game_time: u32,
    pub last_game_time_update_time: u64,
    pub cur_login_time: u64,
    pub is_online: bool,
    pub is_game_time_locked_by_client: bool,
}

impl PlayerBasicComp {
    pub fn set_player(&self, player: Rc<Player>) {
        self.player.set(Rc::downgrade(&player)).unwrap();
    }

    pub fn player(&self) -> Option<Rc<Player>> {
        self.player.get().map(|p| p.upgrade())?
    }

    #[allow(dead_code)]
    pub fn increase_player_stat_id(&mut self) {
        self.player_stat_id += 1;
    }

    pub fn init(&mut self) {
        self.is_guid_init = true;
        self.last_world_level_limit = self.calc_world_level_limit();
    }

    pub fn on_login(&mut self, _is_new_player: bool) {
        self.cur_login_time = time_utils::get_now();
        self.is_online = true;
        self.is_game_time_locked_by_client = false;
    }

    pub fn on_first_login(&mut self) {
        self.level = 1;
        let now = time_utils::get_now();
        self.register_time = now as u32;
        self.game_time = (7.31 * 60.0) as u32;
        self.last_game_time_update_time = now;
        self.is_weather_locked = true;
        self.is_game_time_locked = true;
        self.last_daily_refresh_time = now as u32;
    }

    pub fn notify_all_data(&self) {
        self.notify_all_open_state_data();
    }

    pub fn notify_all_open_state_data(&self) {
        self.player().unwrap().send_proto(OpenStateUpdateNotify {
            open_state_map: self.open_state_map.clone().into_iter().collect(),
        });
    }

    pub fn notify_server_time(&self) {
        self.player().unwrap().send_proto(ServerTimeNotify {
            server_time: time_utils::get_now_ms(),
        });
    }

    pub fn notify_game_time(&self, player: &Player) {
        player.send_proto(PlayerGameTimeNotify {
            is_home: false,
            uid: self.player().unwrap().uid,
            game_time: self.game_time,
        });
    }

    pub fn gen_guid(&mut self) -> u64 {
        if !self.is_guid_init {
            tracing::error!("called before init");
            return 0;
        }

        let uid = self.player().unwrap().uid;
        self.guid_seq_id += 1;

        ((uid as u64) << 32) | (self.guid_seq_id as u64)
    }

    pub fn check_nick_name(&self, _nick_name: &str) -> Result<(), Retcode> {
        Ok(())
    }

    fn calc_world_level_limit(&self) -> u32 {
        // TODO!
        8
    }

    pub fn from_bin(bin: PlayerBasicCompBin) -> Self {
        Self {
            player: OnceCell::new(),
            level: bin.level,
            exp: bin.exp,
            nickname: bin.nickname,
            total_game_time: bin.total_game_time,
            trans_no_count: bin.trans_no_count,
            is_weather_locked: bin.is_weather_locked,
            is_game_time_locked: bin.is_game_time_locked,
            persist_stamina_limit: bin.persist_stamina_limit,
            cur_persist_stamina: bin.cur_persist_stamina,
            cur_temporary_stamina: bin.cur_temporary_stamina,
            open_state_map: bin.open_state_map.into_iter().collect(),
            player_time_ms: bin.player_time_ms,
            last_login_time: bin.last_login_time,
            register_time: bin.register_time,
            total_login_days: bin.total_login_days,
            update_login_days_time: bin.update_login_days_time,
            reward_taken_level_list: bin.reward_taken_level_list,
            language_type: bin.language_type,
            client_app_version: bin.client_app_version,
            client_device_info: bin.client_device_info,
            client_system_version: bin.client_system_version,
            is_guest: bin.is_guest,
            online_time: bin.online_time,
            is_proficient_player: bin.is_proficient_player,
            set_language_tag: bin.set_language_tag,
            head_image_avatar_id: bin.head_image_avatar_id,
            guid_seq_id: bin.guid_seq_id,
            is_rebate_mail_sent: bin.is_rebate_mail_sent,
            is_rebate_mail_received: bin.is_rebate_mail_received,
            register_cps: bin.register_cps,
            cur_json_climate_type: bin.cur_json_climate_type,
            cur_climate_meter: bin.cur_climate_meter,
            last_logout_time: bin.last_logout_time,
            data_version: bin.data_version,
            last_daily_refresh_time: bin.last_daily_refresh_time,
            is_auto_unlock_specific_equip: bin.is_auto_unlock_specific_equip,
            profile_picture_costume_id: bin.profile_picture_costume_id,
            online_id: bin.online_id,
            psn_id: bin.psn_id,
            last_login_platform: bin.last_login_platform,
            pivot_client_time: bin.pivot_client_time,
            pivot_unix_time: bin.pivot_unix_time,
            client_total_tick_time: bin.client_total_tick_time,
            player_stat_id: bin.player_stat_id,
            ..Default::default()
        }
    }

    pub fn to_bin(&self) -> PlayerBasicCompBin {
        PlayerBasicCompBin {
            level: self.level.clone(),
            exp: self.exp.clone(),
            nickname: self.nickname.clone(),
            total_game_time: self.total_game_time.clone(),
            trans_no_count: self.trans_no_count.clone(),
            is_weather_locked: self.is_weather_locked.clone(),
            is_game_time_locked: self.is_game_time_locked.clone(),
            persist_stamina_limit: self.persist_stamina_limit.clone(),
            cur_persist_stamina: self.cur_persist_stamina.clone(),
            cur_temporary_stamina: self.cur_temporary_stamina.clone(),
            open_state_map: self.open_state_map.clone().into_iter().collect(),
            player_time_ms: self.player_time_ms.clone(),
            last_login_time: self.last_login_time.clone(),
            register_time: self.register_time.clone(),
            total_login_days: self.total_login_days.clone(),
            update_login_days_time: self.update_login_days_time.clone(),
            reward_taken_level_list: self.reward_taken_level_list.clone(),
            language_type: self.language_type.clone(),
            client_app_version: self.client_app_version.clone(),
            client_device_info: self.client_device_info.clone(),
            client_system_version: self.client_system_version.clone(),
            is_guest: self.is_guest.clone(),
            online_time: self.online_time.clone(),
            is_proficient_player: self.is_proficient_player.clone(),
            set_language_tag: self.set_language_tag.clone(),
            head_image_avatar_id: self.head_image_avatar_id.clone(),
            guid_seq_id: self.guid_seq_id.clone(),
            is_rebate_mail_sent: self.is_rebate_mail_sent.clone(),
            is_rebate_mail_received: self.is_rebate_mail_received.clone(),
            register_cps: self.register_cps.clone(),
            cur_json_climate_type: self.cur_json_climate_type.clone(),
            cur_climate_meter: self.cur_climate_meter.clone(),
            last_logout_time: self.last_logout_time.clone(),
            data_version: self.data_version.clone(),
            last_daily_refresh_time: self.last_daily_refresh_time.clone(),
            is_auto_unlock_specific_equip: self.is_auto_unlock_specific_equip.clone(),
            profile_picture_costume_id: self.profile_picture_costume_id.clone(),
            online_id: self.online_id.clone(),
            psn_id: self.psn_id.clone(),
            last_login_platform: self.last_login_platform.clone(),
            pivot_client_time: self.pivot_client_time.clone(),
            pivot_unix_time: self.pivot_unix_time.clone(),
            client_total_tick_time: self.client_total_tick_time.clone(),
            player_stat_id: self.player_stat_id.clone(),
            nickname_audit_bin: None,
        }
    }
}
