//! Unit tests for effect_convert.rs — spell target resolution and
//! EffectDefinition → Effect conversion.

use cardgame::cards::EffectDefinition;
use cardgame::effects::{Effect, EffectTarget, TargetingRule};
use cardgame::engine::{
    effect_def_to_effect_with_target, effect_def_to_triggered_effect, resolve_spell_target,
};
use cardgame::types::{PlayerId, Slot};

// ── resolve_spell_target edge cases ──────────────────────────────────────────
// (basic cases are covered by card_playing_tests.rs)

#[test]
fn test_resolve_target_creature_friendly_slot() {
    // Slots 5-9 target friendly creatures
    let result = resolve_spell_target(
        &TargetingRule::TargetCreature(Default::default()),
        Slot(7),
        PlayerId::PLAYER_ONE,
    );
    let target = result.unwrap();
    match target {
        EffectTarget::Creature { owner, slot } => {
            assert_eq!(owner, PlayerId::PLAYER_ONE);
            assert_eq!(slot, Slot(2)); // 7 - 5 = 2
        }
        _ => panic!("Expected Creature target"),
    }
}

#[test]
fn test_resolve_target_creature_invalid_slot() {
    let result = resolve_spell_target(
        &TargetingRule::TargetCreature(Default::default()),
        Slot(10),
        PlayerId::PLAYER_ONE,
    );
    assert!(result.is_err());
}

#[test]
fn test_resolve_target_enemy_creature_invalid_slot() {
    let result = resolve_spell_target(
        &TargetingRule::TargetEnemyCreature,
        Slot(5),
        PlayerId::PLAYER_ONE,
    );
    assert!(result.is_err());
}

#[test]
fn test_resolve_target_ally_creature_invalid_slot() {
    let result = resolve_spell_target(
        &TargetingRule::TargetAllyCreature,
        Slot(5),
        PlayerId::PLAYER_ONE,
    );
    assert!(result.is_err());
}

#[test]
fn test_resolve_target_any_enemy_player() {
    let result = resolve_spell_target(
        &TargetingRule::TargetAny,
        Slot(10),
        PlayerId::PLAYER_ONE,
    );
    let target = result.unwrap();
    assert!(matches!(target, EffectTarget::Player(p) if p == PlayerId::PLAYER_TWO));
}

#[test]
fn test_resolve_target_any_self_player() {
    let result = resolve_spell_target(
        &TargetingRule::TargetAny,
        Slot(11),
        PlayerId::PLAYER_ONE,
    );
    let target = result.unwrap();
    assert!(matches!(target, EffectTarget::Player(p) if p == PlayerId::PLAYER_ONE));
}

#[test]
fn test_resolve_target_slot_valid() {
    let result = resolve_spell_target(
        &TargetingRule::TargetSlot,
        Slot(3),
        PlayerId::PLAYER_ONE,
    );
    let target = result.unwrap();
    match target {
        EffectTarget::Creature { owner, slot } => {
            assert_eq!(owner, PlayerId::PLAYER_ONE);
            assert_eq!(slot, Slot(3));
        }
        _ => panic!("Expected Creature target for TargetSlot"),
    }
}

#[test]
fn test_resolve_target_slot_invalid() {
    let result = resolve_spell_target(
        &TargetingRule::TargetSlot,
        Slot(5),
        PlayerId::PLAYER_ONE,
    );
    assert!(result.is_err());
}

#[test]
fn test_resolve_player_target_enemy() {
    let result = resolve_spell_target(
        &TargetingRule::TargetPlayer,
        Slot(0),
        PlayerId::PLAYER_ONE,
    );
    let target = result.unwrap();
    assert!(matches!(target, EffectTarget::Player(p) if p == PlayerId::PLAYER_TWO));
}

#[test]
fn test_resolve_player_target_self() {
    let result = resolve_spell_target(
        &TargetingRule::TargetPlayer,
        Slot(1),
        PlayerId::PLAYER_ONE,
    );
    let target = result.unwrap();
    assert!(matches!(target, EffectTarget::Player(p) if p == PlayerId::PLAYER_ONE));
}

// ── effect_def_to_effect_with_target ─────────────────────────────────────────

#[test]
fn test_damage_conversion() {
    let def = EffectDefinition::Damage {
        amount: 3,
        filter: None,
    };
    let target = EffectTarget::Creature {
        owner: PlayerId::PLAYER_TWO,
        slot: Slot(0),
    };
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);

    match effect.unwrap() {
        Effect::Damage {
            target: t,
            amount,
            filter,
        } => {
            assert_eq!(amount, 3);
            assert!(filter.is_none());
            assert!(matches!(t, EffectTarget::Creature { .. }));
        }
        _ => panic!("Expected Damage effect"),
    }
}

#[test]
fn test_heal_conversion() {
    let def = EffectDefinition::Heal {
        amount: 2,
        filter: None,
    };
    let target = EffectTarget::Player(PlayerId::PLAYER_ONE);
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);

    match effect.unwrap() {
        Effect::Heal {
            amount, filter, ..
        } => {
            assert_eq!(amount, 2);
            assert!(filter.is_none());
        }
        _ => panic!("Expected Heal effect"),
    }
}

#[test]
fn test_draw_always_targets_caster() {
    let def = EffectDefinition::Draw { count: 2 };
    // Even though target says player 2, draw should go to caster (player 1)
    let target = EffectTarget::Player(PlayerId::PLAYER_TWO);
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);

    match effect.unwrap() {
        Effect::Draw { player, count } => {
            assert_eq!(player, PlayerId::PLAYER_ONE);
            assert_eq!(count, 2);
        }
        _ => panic!("Expected Draw effect"),
    }
}

#[test]
fn test_buff_stats_conversion() {
    let def = EffectDefinition::BuffStats {
        attack: 2,
        health: 3,
        filter: None,
    };
    let target = EffectTarget::Creature {
        owner: PlayerId::PLAYER_ONE,
        slot: Slot(1),
    };
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);

    match effect.unwrap() {
        Effect::BuffStats {
            attack, health, ..
        } => {
            assert_eq!(attack, 2);
            assert_eq!(health, 3);
        }
        _ => panic!("Expected BuffStats effect"),
    }
}

#[test]
fn test_destroy_conversion() {
    let def = EffectDefinition::Destroy { filter: None };
    let target = EffectTarget::Creature {
        owner: PlayerId::PLAYER_TWO,
        slot: Slot(0),
    };
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);
    assert!(effect.is_some());
}

#[test]
fn test_gain_essence_targets_caster() {
    let def = EffectDefinition::GainEssence { amount: 2 };
    let target = EffectTarget::None;
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);

    match effect.unwrap() {
        Effect::GainEssence { player, amount } => {
            assert_eq!(player, PlayerId::PLAYER_ONE);
            assert_eq!(amount, 2);
        }
        _ => panic!("Expected GainEssence effect"),
    }
}

#[test]
fn test_refresh_creature_conversion() {
    let def = EffectDefinition::RefreshCreature;
    let target = EffectTarget::Creature {
        owner: PlayerId::PLAYER_ONE,
        slot: Slot(2),
    };
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);
    assert!(matches!(effect.unwrap(), Effect::RefreshCreature { .. }));
}

#[test]
fn test_copy_conversion() {
    let def = EffectDefinition::Copy;
    let target = EffectTarget::Creature {
        owner: PlayerId::PLAYER_TWO,
        slot: Slot(0),
    };
    let effect = effect_def_to_effect_with_target(&def, target, PlayerId::PLAYER_ONE);

    match effect.unwrap() {
        Effect::Copy { owner, .. } => {
            assert_eq!(owner, PlayerId::PLAYER_ONE);
        }
        _ => panic!("Expected Copy effect"),
    }
}

// ── effect_def_to_triggered_effect ───────────────────────────────────────────

fn make_ability(targeting: TargetingRule) -> cardgame::cards::AbilityDefinition {
    cardgame::cards::AbilityDefinition {
        trigger: cardgame::effects::Trigger::OnPlay,
        targeting,
        essence_cost: 0,
        effects: vec![],
        conditional_effects: vec![],
    }
}

#[test]
fn test_triggered_damage_defaults_to_all_enemies() {
    let def = EffectDefinition::Damage {
        amount: 1,
        filter: None,
    };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);

    match effect.unwrap() {
        Effect::Damage { target, .. } => {
            assert!(
                matches!(target, EffectTarget::AllEnemyCreatures(_)),
                "NoTarget triggered damage should hit all enemy creatures"
            );
        }
        _ => panic!("Expected Damage effect"),
    }
}

#[test]
fn test_triggered_damage_enemy_player_targeting() {
    let def = EffectDefinition::Damage {
        amount: 2,
        filter: None,
    };
    let ability = make_ability(TargetingRule::TargetEnemyPlayer);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);

    match effect.unwrap() {
        Effect::Damage { target, .. } => {
            assert!(matches!(target, EffectTarget::Player(p) if p == PlayerId::PLAYER_TWO));
        }
        _ => panic!("Expected Damage effect"),
    }
}

#[test]
fn test_triggered_heal_targets_self() {
    let def = EffectDefinition::Heal {
        amount: 2,
        filter: None,
    };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(2), &ability);

    match effect.unwrap() {
        Effect::Heal { target, .. } => {
            assert!(matches!(
                target,
                EffectTarget::Creature {
                    owner: PlayerId::PLAYER_ONE,
                    slot: Slot(2),
                }
            ));
        }
        _ => panic!("Expected Heal effect"),
    }
}

#[test]
fn test_triggered_buff_targets_self() {
    let def = EffectDefinition::BuffStats {
        attack: 1,
        health: 1,
        filter: None,
    };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);

    match effect.unwrap() {
        Effect::BuffStats { target, .. } => {
            assert!(matches!(target, EffectTarget::Creature { slot: Slot(0), .. }));
        }
        _ => panic!("Expected BuffStats effect"),
    }
}

#[test]
fn test_triggered_draw_targets_owner() {
    let def = EffectDefinition::Draw { count: 1 };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_TWO, Slot(0), &ability);

    match effect.unwrap() {
        Effect::Draw { player, count } => {
            assert_eq!(player, PlayerId::PLAYER_TWO);
            assert_eq!(count, 1);
        }
        _ => panic!("Expected Draw effect"),
    }
}

#[test]
fn test_triggered_destroy_returns_none() {
    let def = EffectDefinition::Destroy { filter: None };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);
    assert!(effect.is_none(), "Destroy should return None for triggered abilities");
}

#[test]
fn test_triggered_transform_returns_none() {
    let def = EffectDefinition::Transform {
        into: cardgame::cards::TokenDef {
            name: "Token".to_string(),
            attack: 1,
            health: 1,
            keywords: vec![],
            abilities: vec![],
        },
    };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);
    assert!(effect.is_none(), "Transform should return None for triggered abilities");
}

#[test]
fn test_triggered_copy_returns_none() {
    let def = EffectDefinition::Copy;
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);
    assert!(effect.is_none(), "Copy should return None for triggered abilities");
}

#[test]
fn test_triggered_gain_essence() {
    let def = EffectDefinition::GainEssence { amount: 1 };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);

    match effect.unwrap() {
        Effect::GainEssence { player, amount } => {
            assert_eq!(player, PlayerId::PLAYER_ONE);
            assert_eq!(amount, 1);
        }
        _ => panic!("Expected GainEssence effect"),
    }
}

#[test]
fn test_triggered_bounce_defaults_to_enemy_creatures() {
    let def = EffectDefinition::Bounce { filter: None };
    let ability = make_ability(TargetingRule::NoTarget);
    let effect =
        effect_def_to_triggered_effect(&def, PlayerId::PLAYER_ONE, Slot(0), &ability);

    match effect.unwrap() {
        Effect::Bounce { target, .. } => {
            assert!(matches!(target, EffectTarget::AllEnemyCreatures(_)));
        }
        _ => panic!("Expected Bounce effect"),
    }
}
