use crate::constants::MAX_ARROW_FLIGHT;
use crate::map::{Room, WumpusMap};
use crate::play::game::ExitStatus;
use crate::play::game::ExitStatus::NormalExit;
use crate::play::magic::{is_magic_tunnel_for_some_reason, select_random_room};
use crate::play::messages::{kill_wump, no_arrows, shoot_self, wump_kill};
use crate::play::movement::move_wump;
use crate::play::Things;
use crate::{dprint, Args};
use rand::prelude::ThreadRng;
use rand::Rng;

fn follow_arrow_flight<'a>(
    rooms: &[usize],
    things: &mut Things,
    wumpus_map: &'a WumpusMap,
    rng: &mut ThreadRng,
) -> anyhow::Result<&'a Room> {
    let player_room = wumpus_map.get_room(&things.player);
    let mut current_room = player_room;
    for (room_count, arrow_room) in rooms
        .iter()
        .map(|&r| wumpus_map.get_room_from_room_number(r))
        .enumerate()
    {
        let arrow_next_room = arrow_room?;
        let arrow_can_advance = current_room.connects_to(arrow_next_room, wumpus_map)?;
        dprint!(
            "Following arrow flight: count {}, room {}, can advance {}",
            room_count,
            arrow_next_room.number,
            arrow_can_advance
        );

        if room_count > MAX_ARROW_FLIGHT as usize {
            println!("The arrow wavers in its flight and and can go no further!");
            return Ok(current_room);
        }
        if arrow_can_advance {
            if is_magic_tunnel_for_some_reason(arrow_next_room, wumpus_map)? {
                print!("A faint gleam tells you the arrow has gone through a magic tunnel!\n");
                current_room = select_random_room(wumpus_map, rng)?;
            } else {
                current_room = arrow_next_room;
            }
        } else {
            // Arrow moves to a random room
            dprint!("Arrow randomized\n");
            let neighbors = current_room.get_vertex(wumpus_map)?.neighbors();
            let links = neighbors.len();
            let next_vertex = neighbors[rng.gen_range(0..links)];
            let random_next_room = wumpus_map.get_room(&next_vertex);

            if next_vertex == player_room.key {
                println!(
                    "*thunk*  The arrow can't find a way from {} to {} and files back into",
                    current_room.number, arrow_next_room.number
                );
                println!("your room!")
            }
            /*cave[arrow_location].tunnel[link] > room_num%d(void)fcave[location].tunnel[link]*/
            else if is_magic_tunnel_for_some_reason(arrow_next_room, wumpus_map)? {
                print!(
                    "*thunk*  The arrow files randomly into a magic tunnel, thence into\n\
                    room {}!\n",
                    arrow_next_room.number
                );
            } else {
                println!(
                    "*thunk*  The arrow can't find a way from {} to {} and files randomly",
                    current_room.number, arrow_next_room.number
                );
                println!("into room {}!", random_next_room.number);
            }
            current_room = random_next_room;
            return Ok(current_room);
        }
        let likelihood_of_proper_archery = rng.gen_range(0..10);
        if room_count == 3 && likelihood_of_proper_archery < 2 {
            print!("Your bowstring breaks!  *twaaaaaang*\n");
            print!("The arrow is weakly shot and can go no further!\n");
            return Ok(current_room);
        } else if room_count == 4 && likelihood_of_proper_archery < 6 {
            print!("The arrow wavers in its flight and and can go no further!\n");
            return Ok(current_room);
        }
    }
    Ok(current_room)
}

#[allow(unused_variables)]
pub fn shoot_arrow(
    rooms: Vec<usize>,
    things: &mut Things,
    arrows: &mut u32,
    wumpus_map: &WumpusMap,
    rng: &mut ThreadRng,
    config: &Args,
) -> anyhow::Result<ExitStatus> {
    /*
     * Implement shooting arrows.  Arrows are shot by the player indicating
     * a space-separated list of rooms that the arrow should pass through;
     * if any of the rooms they specify are not accessible via tunnel from
     * the room the arrow is in, it will instead fly randomly into another
     * room.  If the player hits the wumpus, this routine will indicate
     * such.  If it misses, this routine will *move* the wumpus one room.
     * If it's the last arrow, the player then dies...  Returns 1 if the
     * player has won or died, 0 if nothing has happened.
     */

    let room_total_count = rooms.len();
    if room_total_count == 0 {
        println!("The arrow falls to the ground at your feet!");
        return Ok(ExitStatus::Continue);
    }
    let arrow_landed_room = follow_arrow_flight(&rooms, things, wumpus_map, rng)?;
    dprint!("Arrow landed in room {}.", arrow_landed_room.number);
    /*
     * now we've gotten into the new room let us see if El Wumpo is
     * in the same room ... if so we've a HIT and the player WON!
     */
    *arrows -= 1;
    if things.get_wumpus_location() == arrow_landed_room.key {
        kill_wump();
        Ok(ExitStatus::NormalExit)
    } else if things.player == arrow_landed_room.key {
        shoot_self();
        Ok(ExitStatus::NormalExit)
    } else if *arrows == 0 {
        no_arrows();
        Ok(ExitStatus::NormalExit)
    } else {
        let safety_tolerance = if config.hard { 9 } else { 12 };
        things.wumpus_movement_likelihood += 2;
        if rng.gen_range(0..safety_tolerance) < things.wumpus_movement_likelihood {
            move_wump(things, wumpus_map, rng)?;
            if things.player_wumpus_collision() {
                wump_kill();
                // There was a bug in the original that didn't exit here
                return Ok(NormalExit);
            }
            things.wumpus_movement_likelihood = rng.gen_range(0..3);
        }
        Ok(ExitStatus::Continue)
    }
}