use std::{
    net::Ipv4Addr,
    sync::atomic::{AtomicU32, AtomicU64, Ordering},
};

use common::{
    base::NetService,
    net::{tools::Packet, ServiceType},
    tools::time_utils,
    ServiceQualifier,
};
use parking_lot::RwLock;
use proto::{PacketHead, PlayerLoginReq};

use crate::{gameserver_service, player::PlayerCoroExec};

pub struct PlayerSessionProxy {
    pub uid: u32,
    pub client_ip: String,
    pub flag: String,
    login_packet_head: PacketHead,
    pub login_req: PlayerLoginReq,
    cur_login_rand: AtomicU64,
    pub exec_coroutine: RwLock<Option<PlayerCoroExec>>,
}

impl PlayerSessionProxy {
    pub fn new(uid: u32) -> Self {
        Self {
            uid,
            client_ip: String::new(),
            flag: String::new(),
            login_packet_head: PacketHead::default(),
            login_req: PlayerLoginReq::default(),
            cur_login_rand: AtomicU64::new(0),
            exec_coroutine: RwLock::new(None),
        }
    }

    pub fn set_login_packet_head(&mut self, packet: &Packet) {
        self.login_packet_head = packet.head.clone();
        self.client_ip = Ipv4Addr::from(packet.head.user_ip).to_string();
        self.flag = Self::create_flag_str(self.get_gateserver_id(), packet.head.user_session_id);
    }

    pub fn set_login_req(&mut self, login_req: PlayerLoginReq) {
        self.login_req = login_req;
    }

    pub fn get_gateserver_id(&self) -> u32 {
        self.login_packet_head.sender_app_id
    }

    pub fn get_gateserver_session_id(&self) -> u32 {
        self.login_packet_head.user_session_id
    }

    pub fn get_cur_login_rand(&self) -> u64 {
        self.cur_login_rand.load(Ordering::SeqCst)
    }

    pub fn gen_new_login_rand(&self) {
        let login_rand = do_gen_login_rand();
        self.cur_login_rand.store(login_rand, Ordering::SeqCst);

        if login_rand != 0 {
            tracing::info!(
                "[LOGIN] uid: {} gate login_rand: {} cur_login_rand: {} gatesession: {}",
                self.uid,
                self.login_req.login_rand,
                login_rand,
                &self.flag
            );
        } else {
            tracing::warn!("do_gen_login_rand return 0");
        }
    }

    pub fn send_packet(&self, mut packet: Packet) {
        packet.head = PacketHead {
            user_id: self.uid,
            user_ip: self.login_packet_head.user_ip,
            user_session_id: self.login_packet_head.user_session_id,
            rpc_id: ServiceType::Gateserver as u32,
            ..Default::default()
        };

        let dst_app_id = self.login_packet_head.sender_app_id;
        let _ = gameserver_service()
            .service_box
            .push_packet_to_service_blocking(ServiceQualifier::AppId(dst_app_id), packet);
    }

    pub fn on_connect(&self) {
        tracing::info!(
            "[LOGIN][SESSION] on_connect uid: {} gatesession: {}",
            self.uid,
            &self.flag
        );
    }

    fn create_flag_str(gate_id: u32, gate_session: u32) -> String {
        format!("{gate_id}:{gate_session}")
    }
}

fn do_gen_login_rand() -> u64 {
    static SEQ: AtomicU32 = AtomicU32::new(0);

    let v4 = ((gameserver_service().network_mgr.app_id() >> 16) & 0xFFFF) as u64;
    let v5 = ((SEQ.fetch_add(1, Ordering::SeqCst) + 1) & 0xFFFF) as u64;
    let now = (time_utils::get_now() & 0xFFFFFFFF) as u64;

    v4 << 48 | v5 << 32 | now
}
