use common::cryptography::{rsa_decrypt, rsa_encrypt, rsa_sign};
use rand::RngCore;

use crate::{logic::player::PlayerManager, net::NetSessionState};

use super::*;

pub async fn on_player_get_token(
    session: &NetSession,
    player_mgr: &PlayerManager,
    req: PlayerGetTokenCsReq,
) -> NetResult<PlayerGetTokenScRsp> {
    session.set_state(NetSessionState::PlayerGetTokenCsReq);

    let uid = player_mgr
        .authorize(&req.account_uid, &req.token, req.platform_type)
        .await?;

    let client_rand_key = rbase64::decode(&req.client_rand_key).map_err(|_| Retcode::RetFail)?;
    let client_rand_key = u64::from_le_bytes(rsa_decrypt(&client_rand_key).try_into().unwrap());
    let server_rand_key = rand::thread_rng().next_u64();

    session.set_session_key(client_rand_key ^ server_rand_key);
    session.set_account_uid(req.account_uid);
    session.set_player_uid(uid);
    session.set_state(NetSessionState::PlayerGetTokenScRsp);

    Ok(PlayerGetTokenScRsp {
        retcode: Retcode::RetSucc.into(),
        uid,
        server_rand_key: rbase64::encode(&rsa_encrypt(&server_rand_key.to_le_bytes())),
        sign: rbase64::encode(&rsa_sign(&server_rand_key.to_le_bytes())),
        ..Default::default()
    })
}

pub async fn on_player_login(
    session: &NetSession,
    player_mgr: &PlayerManager,
    _req: PlayerLoginCsReq,
) -> NetResult<PlayerLoginScRsp> {
    session.set_state(NetSessionState::PlayerLoginCsReq);

    let player_uid = *session.player_uid().ok_or(Retcode::RetFail)?;
    let player_lock = player_mgr
        .get_player(player_uid)
        .await
        .ok_or(Retcode::RetFail)?;

    let mut player = player_lock.lock().await;
    player.set_current_session(session.id());
    player.on_login();

    session.set_state(NetSessionState::StartBasicsReq);
    Ok(PlayerLoginScRsp {
        retcode: Retcode::RetSucc.into(),
        ..Default::default()
    })
}
