//! Action, event, and combat serialization into DTOs.

use super::*;
use crate::client_api::GameEvent;
use crate::core::state::{GameResult, GameState};
use crate::core::types::Slot;
use crate::{Action, CardDatabase, Target};

// =============================================================================
// Action & Event Conversion
// =============================================================================

/// Convert a game action to an ActionInfo DTO.
pub fn action_to_info(action: &Action, index: u16) -> ActionInfo {
    match action {
        Action::PlayCard { hand_index, slot } => ActionInfo {
            index,
            action_type: "play_card".to_string(),
            description: format!("Play card from hand {} to slot {}", hand_index, slot.0),
            source_slot: None,
            target_slot: Some(slot.0),
            hand_index: Some(*hand_index),
            card_id: None,
            ability_index: None,
        },
        Action::PlayCardOverload { hand_index, slot } => ActionInfo {
            index,
            action_type: "play_card_overload".to_string(),
            description: format!("Overload card from hand {} to slot {} (pay life)", hand_index, slot.0),
            source_slot: None,
            target_slot: Some(slot.0),
            hand_index: Some(*hand_index),
            card_id: None,
            ability_index: None,
        },
        Action::Attack { attacker, defender } => ActionInfo {
            index,
            action_type: "attack".to_string(),
            description: format!("Attack slot {} with creature in slot {}", defender.0, attacker.0),
            source_slot: Some(attacker.0),
            target_slot: Some(defender.0),
            hand_index: None,
            card_id: None,
            ability_index: None,
        },
        Action::UseAbility { slot, ability_index, target } => {
            let (target_desc, target_slot) = match target {
                Target::NoTarget => ("".to_string(), None),
                Target::EnemySlot(s) => (format!(" on enemy slot {}", s.0), Some(s.0)),
                Target::AllySlot(s) => (format!(" on ally slot {}", s.0), Some(s.0)),
            };
            ActionInfo {
                index,
                action_type: "use_ability".to_string(),
                description: format!("Use ability {} from slot {}{}", ability_index, slot.0, target_desc),
                source_slot: Some(slot.0),
                target_slot,
                hand_index: None,
                card_id: None,
                ability_index: Some(*ability_index),
            }
        }
        Action::CommanderInsight => ActionInfo {
            index,
            action_type: "commander_insight".to_string(),
            description: "Commander's Insight - draw a card for 4 essence".to_string(),
            source_slot: None,
            target_slot: None,
            hand_index: None,
            card_id: None,
            ability_index: None,
        },
        Action::EndTurn => ActionInfo {
            index,
            action_type: "end_turn".to_string(),
            description: "End turn".to_string(),
            source_slot: None,
            target_slot: None,
            hand_index: None,
            card_id: None,
            ability_index: None,
        },
    }
}

/// Convert a game action to an ActionInfo DTO with human-readable names from game state.
///
/// Call this BEFORE applying the action so the pre-action state is used for lookups.
pub fn action_to_info_with_state(
    action: &Action,
    index: u16,
    state: &GameState,
    card_db: &CardDatabase,
) -> ActionInfo {
    let active = state.active_player.index();
    let opponent = 1 - active;

    let lookup = |card_id: crate::core::types::CardId| -> String {
        card_db.get(card_id).map(|c| c.name.clone()).unwrap_or_default()
    };

    let creature_name = |player_idx: usize, slot: Slot| -> Option<String> {
        state.players[player_idx]
            .creatures
            .iter()
            .find(|c| c.slot == slot)
            .map(|c| lookup(c.card_id))
    };

    match action {
        Action::PlayCard { hand_index, slot } => {
            let name = state.players[active]
                .hand
                .get(*hand_index as usize)
                .map(|ci| lookup(ci.card_id))
                .unwrap_or_else(|| "?".to_string());
            ActionInfo {
                index,
                action_type: "play_card".to_string(),
                description: format!("Play {} to slot {}", name, slot.0 + 1),
                source_slot: None,
                target_slot: Some(slot.0),
                hand_index: Some(*hand_index),
                card_id: None,
                ability_index: None,
            }
        }
        Action::PlayCardOverload { hand_index, slot } => {
            let name = state.players[active]
                .hand
                .get(*hand_index as usize)
                .map(|ci| lookup(ci.card_id))
                .unwrap_or_else(|| "?".to_string());
            ActionInfo {
                index,
                action_type: "play_card_overload".to_string(),
                description: format!("Overload {} to slot {} (pay life)", name, slot.0 + 1),
                source_slot: None,
                target_slot: Some(slot.0),
                hand_index: Some(*hand_index),
                card_id: None,
                ability_index: None,
            }
        }
        Action::Attack { attacker, defender } => {
            let attacker_name = creature_name(active, *attacker)
                .unwrap_or_else(|| format!("slot {}", attacker.0 + 1));
            let defender_name = creature_name(opponent, *defender)
                .unwrap_or_else(|| "face".to_string());
            ActionInfo {
                index,
                action_type: "attack".to_string(),
                description: format!("{} attacks {}", attacker_name, defender_name),
                source_slot: Some(attacker.0),
                target_slot: Some(defender.0),
                hand_index: None,
                card_id: None,
                ability_index: None,
            }
        }
        Action::UseAbility { slot, ability_index, target } => {
            let caster_name = creature_name(active, *slot)
                .unwrap_or_else(|| format!("slot {}", slot.0 + 1));
            let (target_desc, target_slot) = match target {
                Target::NoTarget => ("".to_string(), None),
                Target::EnemySlot(s) => {
                    let tname = creature_name(opponent, *s)
                        .unwrap_or_else(|| format!("enemy slot {}", s.0 + 1));
                    (format!(" → {}", tname), Some(s.0))
                }
                Target::AllySlot(s) => {
                    let tname = creature_name(active, *s)
                        .unwrap_or_else(|| format!("ally slot {}", s.0 + 1));
                    (format!(" → {}", tname), Some(s.0))
                }
            };
            ActionInfo {
                index,
                action_type: "use_ability".to_string(),
                description: format!("{} uses ability {}{}", caster_name, ability_index, target_desc),
                source_slot: Some(slot.0),
                target_slot,
                hand_index: None,
                card_id: None,
                ability_index: Some(*ability_index),
            }
        }
        _ => action_to_info(action, index),
    }
}

/// Convert a GameEvent to a GameEventDto.
pub fn game_event_to_dto(event: &GameEvent) -> GameEventDto {
    let (event_type, data) = match event {
        GameEvent::GameStarted { seed, mode, .. } => (
            "game_started",
            serde_json::json!({ "seed": seed, "mode": format!("{:?}", mode) }),
        ),
        GameEvent::TurnStarted { player, turn_number, .. } => (
            "turn_started",
            serde_json::json!({ "player": player.0 + 1, "turn": turn_number }),
        ),
        GameEvent::TurnEnded { player, turn_number } => (
            "turn_ended",
            serde_json::json!({ "player": player.0 + 1, "turn": turn_number }),
        ),
        GameEvent::ActionTaken { player, action, turn } => (
            "action_taken",
            serde_json::json!({
                "player": player.0 + 1,
                "action": format!("{:?}", action),
                "turn": turn
            }),
        ),
        GameEvent::CreatureSpawned { player, slot, card_id, .. } => (
            "creature_spawned",
            serde_json::json!({ "player": player.0 + 1, "slot": slot.0, "card_id": card_id.0 }),
        ),
        GameEvent::CreatureDied { player, slot, card_id, name, .. } => (
            "creature_died",
            serde_json::json!({ "player": player.0 + 1, "slot": slot.0, "card_id": card_id.0, "name": name }),
        ),
        GameEvent::LifeChanged { player, old_life, new_life, source } => (
            "life_changed",
            serde_json::json!({
                "player": player.0 + 1,
                "old": old_life,
                "new": new_life,
                "source": format!("{:?}", source)
            }),
        ),
        GameEvent::KeywordActivated { player, slot, keyword, value, target_player, target_slot } => (
            "keyword_activated",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "keyword": format!("{:?}", keyword),
                "value": value,
                "target_player": target_player.map(|p| p.0 + 1),
                "target_slot": target_slot.map(|s| s.0)
            }),
        ),
        GameEvent::GameEnded { result, final_turn } => {
            let (winner, reason) = match result {
                GameResult::Win { winner, reason } => (Some(winner.0 + 1), format!("{:?}", reason)),
                GameResult::Draw => (None, "draw".to_string()),
            };
            (
                "game_ended",
                serde_json::json!({
                    "winner": winner,
                    "reason": reason,
                    "final_turn": final_turn
                }),
            )
        }
        GameEvent::CardDrawn { player, card_id, hand_position, cards_remaining_in_deck } => (
            "card_drawn",
            serde_json::json!({
                "player": player.0 + 1,
                "cardId": card_id.0,
                "handPosition": hand_position,
                "deckRemaining": cards_remaining_in_deck
            }),
        ),
        GameEvent::CardPlayed { player, card_id, hand_index, target_slot, essence_cost } => (
            "card_played",
            serde_json::json!({
                "player": player.0 + 1,
                "cardId": card_id.0,
                "handIndex": hand_index,
                "targetSlot": target_slot.0,
                "essenceCost": essence_cost
            }),
        ),
        GameEvent::CardDiscarded { player, card_id, reason } => (
            "card_discarded",
            serde_json::json!({
                "player": player.0 + 1,
                "cardId": card_id.0,
                "reason": format!("{:?}", reason)
            }),
        ),
        GameEvent::CreatureDamaged { player, slot, name, damage, new_health, source } => (
            "creature_damaged",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "name": name,
                "damage": damage,
                "newHealth": new_health,
                "source": format!("{:?}", source)
            }),
        ),
        GameEvent::CreatureHealed { player, slot, name, amount, new_health } => (
            "creature_healed",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "name": name,
                "amount": amount,
                "newHealth": new_health
            }),
        ),
        GameEvent::CreatureStatsChanged { player, slot, name, old_attack, new_attack, old_health, new_health } => (
            "creature_stats_changed",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "name": name,
                "oldAttack": old_attack,
                "newAttack": new_attack,
                "oldHealth": old_health,
                "newHealth": new_health
            }),
        ),
        GameEvent::CreatureKeywordsChanged { player, slot, old_keywords, new_keywords } => (
            "creature_keywords_changed",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "oldKeywords": format!("{:?}", old_keywords),
                "newKeywords": format!("{:?}", new_keywords)
            }),
        ),
        GameEvent::CreatureBounced { player, slot, card_id } => (
            "creature_bounced",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "cardId": card_id.0
            }),
        ),
        GameEvent::CreatureLeveledUp { player, slot, card_id, new_attack, new_health } => (
            "creature_leveled_up",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "cardId": card_id.0,
                "newAttack": new_attack,
                "newHealth": new_health
            }),
        ),
        GameEvent::CombatStarted { attacker_player, attacker_slot, defender_player, defender_slot } => (
            "combat_started",
            serde_json::json!({
                "attackerPlayer": attacker_player.0 + 1,
                "attackerSlot": attacker_slot.0,
                "defenderPlayer": defender_player.0 + 1,
                "defenderSlot": defender_slot.0
            }),
        ),
        GameEvent::CombatResolved { attacker_damage_dealt, defender_damage_dealt, attacker_died, defender_died } => (
            "combat_resolved",
            serde_json::json!({
                "attackerDamageDealt": attacker_damage_dealt,
                "defenderDamageDealt": defender_damage_dealt,
                "attackerDied": attacker_died,
                "defenderDied": defender_died
            }),
        ),
        GameEvent::SupportPlaced { player, slot, card_id, durability } => (
            "support_placed",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "cardId": card_id.0,
                "durability": durability
            }),
        ),
        GameEvent::SupportDurabilityChanged { player, slot, old_durability, new_durability } => (
            "support_durability_changed",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "oldDurability": old_durability,
                "newDurability": new_durability
            }),
        ),
        GameEvent::SupportRemoved { player, slot, card_id, reason } => (
            "support_removed",
            serde_json::json!({
                "player": player.0 + 1,
                "slot": slot.0,
                "cardId": card_id.0,
                "reason": format!("{:?}", reason)
            }),
        ),
        GameEvent::EssenceChanged { player, old_essence, new_essence, max_essence } => (
            "essence_changed",
            serde_json::json!({
                "player": player.0 + 1,
                "old": old_essence,
                "new": new_essence,
                "max": max_essence
            }),
        ),
        GameEvent::ActionPointsChanged { player, old_ap, new_ap } => (
            "action_points_changed",
            serde_json::json!({
                "player": player.0 + 1,
                "oldAp": old_ap,
                "newAp": new_ap
            }),
        ),
        GameEvent::AbilityTriggered { source_player, source_slot, card_id, trigger_name } => (
            "ability_triggered",
            serde_json::json!({
                "player": source_player.0 + 1,
                "slot": source_slot.0,
                "cardId": card_id.0,
                "triggerName": trigger_name
            }),
        ),
        GameEvent::SpellEffectApplied { card_id, effect_description } => (
            "spell_effect_applied",
            serde_json::json!({
                "cardId": card_id.0,
                "effectDescription": effect_description
            }),
        ),
    };

    GameEventDto {
        event_type: event_type.to_string(),
        data,
    }
}

/// Convert a CombatTrace to its DTO for the action log.
pub fn combat_trace_to_dto(trace: &crate::core::tracing::CombatTrace) -> CombatTraceDto {
    let steps = trace
        .steps
        .iter()
        .map(|s| CombatStepDto {
            phase: s.phase.to_string(),
            description: s.description.clone(),
            damage_dealt: s.damage_dealt,
            healing: s.healing,
            death: s.death.clone(),
        })
        .collect();

    CombatTraceDto {
        attacker_slot: trace.attacker_slot.0,
        attacker_attack: trace.attacker_attack,
        attacker_health: trace.attacker_health,
        defender_slot: trace.defender_slot.0,
        defender_attack: trace.defender_attack,
        defender_health: trace.defender_health,
        is_face_attack: trace.is_face_attack,
        steps,
        attacker_died: trace.attacker_died,
        defender_died: trace.defender_died,
        face_damage: trace.face_damage,
        attacker_healed: trace.attacker_healed,
    }
}
