use tokio::task;

use crate::args::Args;
use crate::data::{BotType, CurrentGameStatus, GameRules};
use crate::human_player_ws::ServerMessage;
use crate::server::start_server;
use crate::test::bot_client::ClientEndResult;
use crate::test::{bot_client, TestPort};
use crate::utils::network_utils::wait_for_port;

#[tokio::test]
async fn full_game() {
    let _ = env_logger::builder().is_test(true).try_init();

    let local_set = task::LocalSet::new();
    local_set
        .run_until(async move {
            task::spawn_local(start_server(Args::for_test(
                TestPort::IntermediateBotFullGame,
            )));
            wait_for_port(TestPort::IntermediateBotFullGame.port()).await;

            let mut curr_status = CurrentGameStatus::default();

            let res = bot_client::BotClient::new(TestPort::IntermediateBotFullGame.as_url())
                .with_rules(GameRules::random_players_rules().with_bot_type(BotType::Intermediate))
                .with_server_msg_callback(move |msg| match msg {
                    ServerMessage::OpponentMustFire { status } => {
                        curr_status = status.clone();
                    }
                    ServerMessage::OpponentFireResult { pos, .. } => {
                        let pending_sunk_loc =
                            curr_status.your_map.get_successful_but_un_sunk_locations();

                        if !pending_sunk_loc.is_empty() {
                            log::debug!("Check if fire was smart...");
                            assert!(pending_sunk_loc.iter().any(|l| pos.dist_with(l) <= 1))
                        }
                    }
                    _ => {}
                })
                .run_client()
                .await
                .unwrap();
            assert!(matches!(res, ClientEndResult::Finished { .. }));
        })
        .await;
}