use data::tables::{self, AvatarBaseID};
use proto::{AddAvatarPerformType, AddAvatarScNotify, PlayerSyncScNotify};

use crate::ServerState;

use super::ArgSlice;

pub async fn add(
    args: ArgSlice<'_>,
    state: &ServerState,
) -> Result<String, Box<dyn std::error::Error>> {
    const USAGE: &str = "Usage: avatar add [player_uid] [avatar_id]";

    if args.len() != 2 {
        return Ok(USAGE.to_string());
    }

    let uid = args[0].parse::<u32>()?;
    let avatar_id = args[1].parse::<u32>()?;
    let Ok(avatar_id) = AvatarBaseID::new(avatar_id) else {
        return Ok(format!("avatar with id {avatar_id} doesn't exist"));
    };

    let Some(player_lock) = state.player_mgr.get_player(uid).await else {
        return Ok(String::from("player not found"));
    };

    let (session_id, avatar_sync) = {
        let mut player = player_lock.lock().await;

        player.role_model.add_avatar(avatar_id);
        (player.current_session_id(), player.role_model.avatar_sync())
    };

    if let Some(session) = session_id.map(|id| state.session_mgr.get(id)).flatten() {
        session
            .notify(AddAvatarScNotify {
                avatar_id: avatar_id.value(),
                perform_type: AddAvatarPerformType::Gacha.into(),
                ..Default::default()
            })
            .await?;

        session
            .notify(PlayerSyncScNotify {
                avatar: Some(avatar_sync),
                ..Default::default()
            })
            .await?;
    } else {
        state.player_mgr.save_and_remove(uid).await;
    }

    Ok(format!(
        "successfully added avatar {avatar_id} to player {uid}"
    ))
}

pub async fn add_all(
    args: ArgSlice<'_>,
    state: &ServerState,
) -> Result<String, Box<dyn std::error::Error>> {
    const USAGE: &str = "Usage: avatar add_all [player_uid]";

    if args.len() != 1 {
        return Ok(USAGE.to_string());
    }

    let uid = args[0].parse::<u32>()?;

    let Some(player_lock) = state.player_mgr.get_player(uid).await else {
        return Ok(String::from("player not found"));
    };

    let (session_id, avatar_sync, avatar_id_list) = {
        let mut player = player_lock.lock().await;

        let avatar_id_list = tables::avatar_base_template_tb::iter()
            .filter(|tmpl| tmpl.id.value() < 2000 && !player.role_model.has_avatar(tmpl.id))
            .map(|tmpl| tmpl.id)
            .collect::<Vec<_>>();

        avatar_id_list
            .iter()
            .for_each(|id| player.role_model.add_avatar(*id));

        (
            player.current_session_id(),
            player.role_model.avatar_sync(),
            avatar_id_list,
        )
    };

    if let Some(session) = session_id.map(|id| state.session_mgr.get(id)).flatten() {
        for id in avatar_id_list {
            session
                .notify(AddAvatarScNotify {
                    avatar_id: id.value(),
                    perform_type: AddAvatarPerformType::ShowPopup.into(),
                    ..Default::default()
                })
                .await?;
        }

        session
            .notify(PlayerSyncScNotify {
                avatar: Some(avatar_sync),
                ..Default::default()
            })
            .await?;
    } else {
        state.player_mgr.save_and_remove(uid).await;
    }

    Ok(format!("successfully added all avatars to player {uid}"))
}
