use crate::{
    models::user::{wristpad_hack_data, xp_to_hack_slots},
    services::skills::calculate_total_stats_skills_for_user,
    static_content::room::room_map_by_code,
};

use super::{
    get_player_item_or_fail, user_error, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
};
use ansi::ansi;
use async_trait::async_trait;

pub struct Verb;
#[async_trait]
impl UserVerb for Verb {
    async fn handle(
        self: &Self,
        ctx: &mut VerbContext,
        _verb: &str,
        remaining: &str,
    ) -> UResult<()> {
        let player_item = get_player_item_or_fail(ctx).await?;
        let (loc_type, loc_code) = player_item
            .location
            .split_once("/")
            .ok_or_else(|| UserError("Your location is invalid".to_owned()))?;
        if loc_type != "room" {
            user_error("You can't find a hacking unit here.".to_owned())?;
        }

        let room = room_map_by_code()
            .get(&loc_code)
            .ok_or_else(|| UserError("Your location no longer exists!".to_owned()))?;

        let allowed_hack = room
            .wristpad_hack_allowed
            .as_ref()
            .ok_or_else(|| UserError("You can't find a hacking unit here.".to_owned()))?;
        let hack_data = wristpad_hack_data()
            .get(allowed_hack)
            .ok_or_else(|| UserError("The hacking unit is currently broken.".to_owned()))?;
        if hack_data.name.to_lowercase() != remaining.trim() {
            user_error(format!(
                ansi!("The equipment here only allows you to <bold>hack {}<reset>"),
                &hack_data.name
            ))?;
        }

        let user = ctx
            .user_dat
            .as_mut()
            .ok_or(UserError("Please log in first".to_owned()))?;

        let slots_available = xp_to_hack_slots(player_item.total_xp) as usize;
        let slots_used = user.wristpad_hacks.len();
        if slots_used >= slots_available {
            user_error(format!(
                "Your wristpad crashes and reboots, flashing up an error that \
                 there was no space to install the hack. [You only have {} slots \
                 total on your wristpad to install hacks - try getting some \
                 more experience to earn more]",
                slots_available
            ))?;
        }

        if user.wristpad_hacks.contains(&allowed_hack) {
            user_error(
                "Your wristpad crashes and reboots, flashing up an error that \
                 the same hack was already found on the device."
                    .to_owned(),
            )?;
        }

        user.wristpad_hacks.push(allowed_hack.clone());
        ctx.trans.save_user_model(&user).await?;

        let mut player_mut = (*player_item).clone();
        calculate_total_stats_skills_for_user(&mut player_mut, user);
        ctx.trans.save_item_model(&player_mut).await?;

        ctx.trans
            .queue_for_session(
                &ctx.session,
                Some(&format!(
                    "Your wristpad beeps and reboots. You notice new icon on \
                     it indicating the {} hack has been applied succesfully!\n",
                    hack_data.name
                )),
            )
            .await?;

        Ok(())
    }
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;