//! Unit tests for bot factory and BotType.

use std::str::FromStr;

use cardgame::bots::factory::{create_bot, BotType};
use cardgame::bots::{AlphaBetaConfig, MctsConfig};
use cardgame::cards::CardDatabase;
use cardgame::decks::Faction;
use cardgame::engine::GameEngine;
use cardgame::state::GameMode;
use cardgame::types::CardId;

const DEFAULT_COMMANDER: CardId = CardId(5000);

fn load_test_db() -> CardDatabase {
    CardDatabase::load_with_commanders(
        cardgame::data_dir().join("cards/core_set"),
        cardgame::data_dir().join("commanders"),
    )
    .expect("Failed to load cards with commanders")
}

fn test_deck() -> Vec<CardId> {
    let valid_ids = [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 15, 32, 33, 40];
    (0..20)
        .map(|i| CardId(valid_ids[i % valid_ids.len()] as u16))
        .collect()
}

// --- BotType Parsing Tests ---

#[test]
fn test_parse_random() {
    assert_eq!(BotType::from_str("random").unwrap(), BotType::Random);
    assert_eq!(BotType::from_str("Random").unwrap(), BotType::Random);
    assert_eq!(BotType::from_str("RANDOM").unwrap(), BotType::Random);
}

#[test]
fn test_parse_greedy() {
    assert_eq!(BotType::from_str("greedy").unwrap(), BotType::Greedy);
}

#[test]
fn test_parse_mcts() {
    assert_eq!(BotType::from_str("mcts").unwrap(), BotType::Mcts);
}

#[test]
fn test_parse_alphabeta_variants() {
    assert_eq!(BotType::from_str("alphabeta").unwrap(), BotType::AlphaBeta);
    assert_eq!(
        BotType::from_str("alpha-beta").unwrap(),
        BotType::AlphaBeta
    );
    assert_eq!(BotType::from_str("ab").unwrap(), BotType::AlphaBeta);
}

#[test]
fn test_parse_agent_specialists() {
    assert_eq!(
        BotType::from_str("agent-argentum").unwrap(),
        BotType::AgentSpecialist(Faction::Argentum)
    );
    assert_eq!(
        BotType::from_str("agent-symbiote").unwrap(),
        BotType::AgentSpecialist(Faction::Symbiote)
    );
    assert_eq!(
        BotType::from_str("agent-obsidion").unwrap(),
        BotType::AgentSpecialist(Faction::Obsidion)
    );
}

#[test]
fn test_parse_agent_generalist() {
    assert_eq!(
        BotType::from_str("agent-generalist").unwrap(),
        BotType::AgentGeneralist
    );
}

#[test]
fn test_parse_unknown() {
    let result = BotType::from_str("unknown_bot");
    assert!(result.is_err());
    let err = result.unwrap_err();
    assert!(err.to_string().contains("unknown_bot"));
}

// --- BotType Properties ---

#[test]
fn test_bot_type_names() {
    assert_eq!(BotType::Random.name(), "RandomBot");
    assert_eq!(BotType::Greedy.name(), "GreedyBot");
    assert_eq!(BotType::Mcts.name(), "MctsBot");
    assert_eq!(BotType::AlphaBeta.name(), "AlphaBetaBot");
    assert_eq!(
        BotType::AgentSpecialist(Faction::Argentum).name(),
        "Agent-Argentum"
    );
    assert_eq!(BotType::AgentGeneralist.name(), "Agent-Generalist");
}

#[test]
fn test_uses_mcts() {
    assert!(!BotType::Random.uses_mcts());
    assert!(!BotType::Greedy.uses_mcts());
    assert!(BotType::Mcts.uses_mcts());
    assert!(!BotType::AlphaBeta.uses_mcts());
    assert!(BotType::AgentSpecialist(Faction::Argentum).uses_mcts());
    assert!(BotType::AgentGeneralist.uses_mcts());
}

#[test]
fn test_is_competitive() {
    assert!(!BotType::Random.is_competitive());
    assert!(!BotType::Greedy.is_competitive());
    assert!(BotType::Mcts.is_competitive());
    assert!(BotType::AlphaBeta.is_competitive());
    assert!(BotType::AgentSpecialist(Faction::Symbiote).is_competitive());
    assert!(BotType::AgentGeneralist.is_competitive());
}

#[test]
fn test_agent_weights_path() {
    assert!(BotType::Random.agent_weights_path().is_none());
    assert!(BotType::Greedy.agent_weights_path().is_none());
    assert!(BotType::Mcts.agent_weights_path().is_none());
    assert!(BotType::AlphaBeta.agent_weights_path().is_none());

    let argentum_path = BotType::AgentSpecialist(Faction::Argentum)
        .agent_weights_path()
        .unwrap();
    assert!(argentum_path
        .to_str()
        .unwrap()
        .contains("specialists/argentum"));

    let generalist_path = BotType::AgentGeneralist.agent_weights_path().unwrap();
    assert!(generalist_path
        .to_str()
        .unwrap()
        .contains("generalist.toml"));
}

#[test]
fn test_faction() {
    assert!(BotType::Random.faction().is_none());
    assert_eq!(
        BotType::AgentSpecialist(Faction::Obsidion).faction(),
        Some(Faction::Obsidion)
    );
    assert!(BotType::AgentGeneralist.faction().is_none());
}

#[test]
fn test_all_names() {
    let names = BotType::all_names();
    assert!(names.contains(&"random"));
    assert!(names.contains(&"greedy"));
    assert!(names.contains(&"mcts"));
    assert!(names.contains(&"alphabeta"));
    assert!(names.contains(&"agent-generalist"));
}

// --- Bot Creation Tests ---

#[test]
fn test_create_random_bot() {
    let card_db = load_test_db();
    let bot = create_bot(
        &card_db,
        &BotType::Random,
        None,
        &MctsConfig::default(),
        &AlphaBetaConfig::default(),
        42,
    );
    assert_eq!(bot.name(), "RandomBot");
}

#[test]
fn test_create_greedy_bot() {
    let card_db = load_test_db();
    let bot = create_bot(
        &card_db,
        &BotType::Greedy,
        None,
        &MctsConfig::default(),
        &AlphaBetaConfig::default(),
        42,
    );
    assert_eq!(bot.name(), "GreedyBot");
}

#[test]
fn test_create_alphabeta_bot() {
    let card_db = load_test_db();
    let config = AlphaBetaConfig::fast();
    let bot = create_bot(
        &card_db,
        &BotType::AlphaBeta,
        None,
        &MctsConfig::default(),
        &config,
        42,
    );
    assert!(bot.name().contains("AlphaBeta"));
}

#[test]
fn test_create_mcts_bot() {
    let card_db = load_test_db();
    let config = MctsConfig::fast();
    let bot = create_bot(
        &card_db,
        &BotType::Mcts,
        None,
        &config,
        &AlphaBetaConfig::default(),
        42,
    );
    assert!(bot.name().contains("Mcts"));
}

#[test]
fn test_created_bot_plays_legal_actions() {
    let card_db = load_test_db();

    let mut engine = GameEngine::new(&card_db);
    engine
        .start_game_raw(
            test_deck(),
            test_deck(),
            DEFAULT_COMMANDER,
            DEFAULT_COMMANDER,
            12345,
            GameMode::default(),
        )
        .unwrap();

    // Test each bot type produces legal actions
    for bot_type in &[BotType::Random, BotType::Greedy] {
        let mut bot = create_bot(
            &card_db,
            bot_type,
            None,
            &MctsConfig::fast(),
            &AlphaBetaConfig::fast(),
            42,
        );

        let action = bot.select_action_with_engine(&engine);
        let legal_actions = engine.get_legal_actions();
        assert!(
            legal_actions.contains(&action),
            "{} produced illegal action",
            bot.name()
        );
    }
}
