//! Unit tests for actions module.

use cardgame::actions::{Action, Target};
use cardgame::types::Slot;

#[test]
fn test_target_round_trip() {
    // Test all valid target indices
    for i in 0..=10 {
        let target = Target::from_index(i).unwrap();
        assert_eq!(target.to_index(), i);
    }

    // Test specific targets
    assert_eq!(Target::NoTarget.to_index(), 0);
    assert_eq!(Target::EnemySlot(Slot(0)).to_index(), 1);
    assert_eq!(Target::EnemySlot(Slot(4)).to_index(), 5);
    assert_eq!(Target::AllySlot(Slot(0)).to_index(), 6);
    assert_eq!(Target::AllySlot(Slot(4)).to_index(), 10);
}

#[test]
fn test_target_invalid_index() {
    assert!(Target::from_index(11).is_none());
    assert!(Target::from_index(255).is_none());
}

#[test]
fn test_play_card_indices() {
    // PlayCard indices should be 0-119
    for hand_idx in 0u8..10 {
        for slot in 0u8..12 {
            let action = Action::PlayCard {
                hand_index: hand_idx,
                slot: Slot(slot),
            };
            let index = action.to_index();
            assert!(index <= 119, "PlayCard index {} out of range", index);
            assert_eq!(index, hand_idx as u16 * 12 + slot as u16);
        }
    }

    // Verify boundary values
    let first = Action::PlayCard {
        hand_index: 0,
        slot: Slot(0),
    };
    assert_eq!(first.to_index(), 0);

    let last = Action::PlayCard {
        hand_index: 9,
        slot: Slot(11),
    };
    assert_eq!(last.to_index(), 119);
}

#[test]
fn test_attack_indices() {
    // Attack indices should be 120-144
    for attacker in 0u8..5 {
        for defender in 0u8..5 {
            let action = Action::Attack {
                attacker: Slot(attacker),
                defender: Slot(defender),
            };
            let index = action.to_index();
            assert!(
                (120..=144).contains(&index),
                "Attack index {} out of range",
                index
            );
            assert_eq!(index, 120 + attacker as u16 * 5 + defender as u16);
        }
    }

    // Verify boundary values
    let first = Action::Attack {
        attacker: Slot(0),
        defender: Slot(0),
    };
    assert_eq!(first.to_index(), 120);

    let last = Action::Attack {
        attacker: Slot(4),
        defender: Slot(4),
    };
    assert_eq!(last.to_index(), 144);
}

#[test]
fn test_use_ability_indices() {
    // UseAbility indices should be 145-419
    // 5 slots × 5 abilities × 11 targets = 275 combinations
    for slot in 0u8..5 {
        for ability in 0u8..5 {
            for target in 0u8..11 {
                let target_enum = Target::from_index(target).unwrap();
                let action = Action::UseAbility {
                    slot: Slot(slot),
                    ability_index: ability,
                    target: target_enum,
                };
                let index = action.to_index();
                assert!(
                    (145..=419).contains(&index),
                    "UseAbility index {} out of range for slot={}, ability={}, target={}",
                    index,
                    slot,
                    ability,
                    target
                );
                assert_eq!(index, 145 + slot as u16 * 55 + ability as u16 * 11 + target as u16);
            }
        }
    }

    // Verify boundary values
    let first = Action::UseAbility {
        slot: Slot(0),
        ability_index: 0,
        target: Target::NoTarget,
    };
    assert_eq!(first.to_index(), 145);

    // Last valid UseAbility: slot=4, ability=4, target=10 (AllySlot(4))
    // Index = 145 + 4*55 + 4*11 + 10 = 145 + 220 + 44 + 10 = 419
    let last = Action::UseAbility {
        slot: Slot(4),
        ability_index: 4,
        target: Target::AllySlot(Slot(4)),
    };
    assert_eq!(last.to_index(), 419);
}

#[test]
fn test_end_turn_index() {
    let action = Action::EndTurn;
    assert_eq!(action.to_index(), 421);
}

#[test]
fn test_play_card_round_trip() {
    for hand_idx in 0..10 {
        for slot in 0..12 {
            let original = Action::PlayCard {
                hand_index: hand_idx,
                slot: Slot(slot),
            };
            let index = original.to_index();
            let restored = Action::from_index(index).unwrap();
            assert_eq!(original, restored);
        }
    }
}

#[test]
fn test_attack_round_trip() {
    for attacker in 0..5 {
        for defender in 0..5 {
            let original = Action::Attack {
                attacker: Slot(attacker),
                defender: Slot(defender),
            };
            let index = original.to_index();
            let restored = Action::from_index(index).unwrap();
            assert_eq!(original, restored);
        }
    }
}

#[test]
fn test_use_ability_round_trip() {
    // Test all valid UseAbility combinations (5 slots × 5 abilities × 11 targets)
    for slot in 0..5 {
        for ability in 0..5 {
            for target_idx in 0..11 {
                let target = Target::from_index(target_idx).unwrap();
                let original = Action::UseAbility {
                    slot: Slot(slot),
                    ability_index: ability,
                    target,
                };
                let index = original.to_index();
                let restored = Action::from_index(index).unwrap();
                assert_eq!(original, restored);
            }
        }
    }
}

#[test]
fn test_end_turn_round_trip() {
    let original = Action::EndTurn;
    let index = original.to_index();
    let restored = Action::from_index(index).unwrap();
    assert_eq!(original, restored);
}

#[test]
fn test_all_indices_valid() {
    // Every index 0-421 should map to some action or None
    for i in 0u16..Action::ACTION_SPACE_SIZE as u16 {
        let action = Action::from_index(i);
        if let Some(a) = action {
            // Valid actions should round-trip
            assert_eq!(Action::from_index(a.to_index()), Some(a));
        }
    }
}

#[test]
fn test_index_ranges_complete() {
    // Verify all expected indices are covered without gaps

    // PlayCard: 0-119 (120 indices = 10 hand positions * 12 target slots)
    for i in 0u16..=119 {
        assert!(
            Action::from_index(i).is_some(),
            "PlayCard index {} should be valid",
            i
        );
        match Action::from_index(i).unwrap() {
            Action::PlayCard { .. } => {}
            _ => panic!("Index {} should be PlayCard", i),
        }
    }

    // Attack: 120-144 (25 indices = 5 attacker slots * 5 defender slots)
    for i in 120u16..=144 {
        assert!(
            Action::from_index(i).is_some(),
            "Attack index {} should be valid",
            i
        );
        match Action::from_index(i).unwrap() {
            Action::Attack { .. } => {}
            _ => panic!("Index {} should be Attack", i),
        }
    }

    // UseAbility: 145-419 (275 indices = 5 slots * 5 abilities * 11 targets)
    for i in 145u16..=419 {
        assert!(
            Action::from_index(i).is_some(),
            "UseAbility index {} should be valid",
            i
        );
        match Action::from_index(i).unwrap() {
            Action::UseAbility { .. } => {}
            _ => panic!("Index {} should be UseAbility", i),
        }
    }

    // CommanderInsight: 420
    assert!(Action::from_index(420).is_some());
    match Action::from_index(420).unwrap() {
        Action::CommanderInsight => {}
        _ => panic!("Index 420 should be CommanderInsight"),
    }

    // EndTurn: 421
    assert!(Action::from_index(421).is_some());
    match Action::from_index(421).unwrap() {
        Action::EndTurn => {}
        _ => panic!("Index 421 should be EndTurn"),
    }
}

#[test]
fn test_invalid_indices_out_of_range() {
    // All 422 indices are valid in the new action space
    // Test that from_index doesn't panic and returns correct count
    let mut valid_count = 0;
    for i in 0u16..Action::ACTION_SPACE_SIZE as u16 {
        if Action::from_index(i).is_some() {
            valid_count += 1;
        }
    }

    // Expected: 120 (PlayCard) + 25 (Attack) + 275 (UseAbility) + 1 (CommanderInsight) + 1 (EndTurn) = 422
    assert_eq!(valid_count, Action::ACTION_SPACE_SIZE);
}
