import 'dart:async'; // StreamController 사용을 위해 import
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; // For context.read in _prepareNextStage
import '../game/model/entity.dart';
import '../game/model/item.dart';
import '../game/model/status_effect.dart';
import '../game/model/stage.dart';
import '../game/data/item_table.dart';

import '../game/data/enemy_table.dart';
import '../game/data/player_table.dart';
import '../utils/game_math.dart';
import '../game/enums.dart';
import '../game/model/damage_event.dart'; // DamageEvent import
import '../game/model/effect_event.dart'; // EffectEvent import

import '../game/save_manager.dart';
import '../game/config/game_config.dart';
import '../game/config/battle_config.dart'; // Import BattleConfig
import 'shop_provider.dart'; // Import ShopProvider

import '../game/logic/battle_log_manager.dart';
import '../game/logic/combat_calculator.dart';

class EnemyIntent {
  final EnemyActionType type;
  final int value;
  final RiskLevel risk;
  final String description;
  final bool isSuccess;
  final int finalValue;
  bool isApplied; // Mutable flag to prevent double execution

  EnemyIntent({
    required this.type,
    required this.value,
    required this.risk,
    required this.description,
    required this.isSuccess,
    required this.finalValue,
    this.isApplied = false,
  });
}

class BattleProvider with ChangeNotifier {
  late Character player;
  late Character enemy; // Kept for compatibility, active during Battle/Elite

  late StageModel currentStage; // The current stage object
  EnemyIntent? currentEnemyIntent;

  final BattleLogManager _logManager = BattleLogManager();
  bool isPlayerTurn = true;
  int _turnTransactionId = 0; // To prevent async race conditions
  bool skipAnimations = false; // Sync with SettingsProvider

  int stage = 1;
  int turnCount = 1;
  List<Item> rewardOptions = [];
  bool showRewardPopup = false;
  int _lastGoldReward = 0; // New: Stores gold gained from last victory

  List<String> get logs => _logManager.logs;
  int get lastGoldReward => _lastGoldReward;

  void refreshUI() {
    notifyListeners();
  }

  // Damage Event Stream
  final _damageEventController = StreamController<DamageEvent>.broadcast();
  Stream<DamageEvent> get damageStream => _damageEventController.stream;

  // Effect Event Stream
  final _effectEventController = StreamController<EffectEvent>.broadcast();
  Stream<EffectEvent> get effectStream => _effectEventController.stream;

  // Dependency injection
  final ShopProvider shopProvider;

  BattleProvider({required this.shopProvider}) {
    // initializeBattle(); // Do not auto-start logic
  }

  @override
  void dispose() {
    _damageEventController.close(); // StreamController 닫기
    _effectEventController.close();
    super.dispose();
  }

  void loadFromSave(Map<String, dynamic> data) {
    _turnTransactionId++; // Invalidate previous timers
    stage = data['stage'];
    turnCount = data['turnCount'];
    player = Character.fromJson(data['player']);

    _logManager.clear();
    _addLog("Game Loaded! Resuming Stage $stage");

    _prepareNextStage();
    notifyListeners();
  }

  void initializeBattle() {
    _turnTransactionId++; // Invalidate previous timers
    stage = 1;
    turnCount = 1;
    // Load player from PlayerTable
    final playerTemplate = PlayerTable.get("warrior");
    if (playerTemplate != null) {
      player = playerTemplate.createCharacter();
    } else {
      // Fallback if data is missing
      player = Character(
        name: "Player",
        maxHp: 50,
        armor: 0,
        atk: 5,
        baseDefense: 5,
      );
    }

    // Give test gold
    player.gold = GameConfig.startingGold;

    // Add new status effect items for testing
    player.addToInventory(ItemTable.weapons[3].createItem()); // Stunning Hammer
    player.addToInventory(ItemTable.weapons[4].createItem()); // Jagged Dagger
    player.addToInventory(ItemTable.weapons[5].createItem()); // Sunderer Axe
    player.addToInventory(ItemTable.shields[3].createItem()); // Cursed Shield

    _prepareNextStage();
    _logManager.clear();
    _addLog("Game Started! Stage 1");
    notifyListeners();
  }

  void _prepareNextStage() {
    _turnTransactionId++; // Invalidate previous timers
    // Save Game at the start of each stage
    SaveManager.saveGame(this);

    StageType type;

    // Stage Type Logic
    if (stage % GameConfig.eliteStageInterval == 0) {
      type = StageType.elite; // Every 10th stage is a Boss/Elite
    } else if (stage % GameConfig.shopStageInterval == 0) {
      type = StageType.shop; // Every 5th stage is a Shop (except 10, 20...)
    } else if (stage % GameConfig.restStageInterval == 0) {
      type = StageType.rest; // Every 8th stage is a Rest
    } else {
      type = StageType.battle;
    }

    // Prepare Data based on Type
    Character? newEnemy;
    List<Item> shopItems = [];

    if (type == StageType.battle || type == StageType.elite) {
      bool isElite = type == StageType.elite;

      EnemyTemplate template = EnemyTable.getRandomEnemy(
        stage: stage,
        isElite: isElite,
      );

      newEnemy = template.createCharacter(stage: stage);

      // Assign to the main 'enemy' field for UI compatibility
      enemy = newEnemy;
      isPlayerTurn = true;
      showRewardPopup = false;

      _generateEnemyIntent(); // Generate first intent
      _applyEnemyIntentEffects(); // Apply effects if it's a pre-emptive action (Defense)

      _addLog("Stage $stage ($type) started! A wild ${enemy.name} appeared.");
    } else if (type == StageType.shop) {
      // Generate random items for shop using ShopProvider
      shopProvider.generateShopItems(stage);
      shopItems = shopProvider.availableItems;

      // Dummy enemy to prevent null errors in existing UI (until UI is fully updated)
      enemy = Character(name: "Merchant", maxHp: 9999, armor: 0, atk: 0);

      _addLog("Stage $stage: Entered a Shop.");
    } else if (type == StageType.rest) {
      // Dummy enemy
      enemy = Character(name: "Campfire", maxHp: 9999, armor: 0, atk: 0);
      _addLog("Stage $stage: Found a safe resting spot.");
    }

    currentStage = StageModel(
      type: type,
      enemy: newEnemy,
      shopItems: shopItems, // Pass items from ShopProvider
    );
    turnCount = 1;
    notifyListeners();
  }

  Future<void> _onDefeat() async {
    _addLog("Player defeated! Enemy wins!");
    await SaveManager.clearSaveData();
    notifyListeners();
  }

  /// Handle player's action choice
  Future<void> playerAction(ActionType type, RiskLevel risk) async {
    if (!isPlayerTurn || player.isDead || enemy.isDead || showRewardPopup)
      return;

    // 0. Apply Enemy Pre-emptive Defense - REMOVED (Standard Turn-Based Logic)
    // Defense now happens on Enemy's Turn.

    // Update Enemy Status Effects at the start of Player's turn (user request)

    enemy.updateStatusEffects(); // 1. Check for Defense Forbidden status
    if (type == ActionType.defend &&
        player.hasStatus(StatusEffectType.defenseForbidden)) {
      _addLog("Cannot defend! You are under Defense Forbidden status.");
      notifyListeners(); // 상태 변경을 알림
      _endPlayerTurn();
      return;
    }

    isPlayerTurn = false;
    notifyListeners();

    // 2. Process Start-of-Turn Effects (Stun, Bleed)
    bool canAct = _processStartTurnEffects(player);

    if (player.isDead) {
      await _onDefeat();
      return;
    }

    if (!canAct) {
      _endPlayerTurn(); // Skip turn if stunned
      return;
    }

    _addLog("Player chose to ${type.name} with ${risk.name} risk.");

    // Calculate Outcome using CombatCalculator
    int baseValue = (type == ActionType.attack)
        ? player.totalAtk
        : player.totalDefense;

    final result = CombatCalculator.calculateActionOutcome(
      actionType: type, // Pass player's action type
      risk: risk,
      luck: player.totalLuck,
      baseValue: baseValue,
    );

    if (result.success) {
      if (type == ActionType.attack) {
        int damage = result.value;

        final event = EffectEvent(
          id:
              DateTime.now().millisecondsSinceEpoch.toString() +
              Random().nextInt(1000).toString(),
          type: ActionType.attack,
          risk: risk,
          target: EffectTarget.enemy,
          feedbackType: null,
          attacker: player,
          targetEntity: enemy,
          damageValue: damage,
          isSuccess: true,
        );
        _effectEventController.sink.add(
          event,
        ); // No Future.delayed here, BattleScreen will trigger impact
      } else {
        // Defense Success - Impact is immediate, so process it directly
        final event = EffectEvent(
          id:
              DateTime.now().millisecondsSinceEpoch.toString() +
              Random().nextInt(1000).toString(),
          type: ActionType.defend,
          risk: risk,
          target: EffectTarget.player,
          feedbackType: null,
          targetEntity: player, // player is target for defense
          armorGained: result.value,
          attacker: player, // player is attacker in this context
          isSuccess: true,
        );
        _effectEventController.sink.add(event);
        // handleImpact(event); // REMOVED: Driven by UI
      }
    } else {
      // Failure
      final eventId =
          DateTime.now().millisecondsSinceEpoch.toString() +
          Random().nextInt(1000).toString();
      BattleFeedbackType feedbackType = (type == ActionType.attack)
          ? BattleFeedbackType.miss
          : BattleFeedbackType.failed;
      EffectTarget eventTarget = (type == ActionType.attack)
          ? EffectTarget.enemy
          : EffectTarget.player;
      Character eventTargetEntity = (type == ActionType.attack)
          ? enemy
          : player;

      final event = EffectEvent(
        id: eventId,
        type: type,
        risk: risk,
        target: eventTarget,
        feedbackType: feedbackType,
        attacker: player,
        targetEntity: eventTargetEntity,
        isSuccess: false,
      );
      _effectEventController.sink.add(
        event,
      ); // Send event for miss/fail feedback
      _addLog("${player.name}'s ${type.name} ${feedbackType.name}!");
      // handleImpact(event); // REMOVED: Driven by UI
    }

    // Now check for enemy death (if applicable from bleed, or previous impacts)
    if (enemy.isDead) {
      // Check enemy death after player's action
      _onVictory();
      return;
    }

    // _endPlayerTurn(); // REMOVED: Driven by UI via handleImpact
  }

  void _endPlayerTurn() {
    // Update durations at end of turn
    player.updateStatusEffects();

    // Check if enemy is dead from bleed
    if (enemy.isDead) {
      _onVictory();
      return;
    }

    int tid = _turnTransactionId;
    Future.delayed(
      const Duration(milliseconds: GameConfig.animDelayEnemyTurn),
      () {
        if (tid != _turnTransactionId) return;
        _startEnemyTurn();
      },
    );
  }

  // --- Turn Management Phases ---

  // Phase 4: Start Player Turn
  void _startPlayerTurn() {
    // Player Turn Start Logic
    // Armor decay (Player)
    if (player.armor > 0) {
      player.armor = (player.armor * GameConfig.armorDecayRate).toInt();
      _addLog("Player's armor decayed to ${player.armor}.");
    }

    if (player.isDead) {
      _onDefeat();
      return;
    }

    // [New] Apply Pre-emptive Enemy Intent (Defense/Buffs)
    if (currentEnemyIntent != null) {
      final intent = currentEnemyIntent!;
      if (intent.type == EnemyActionType.defend) {
        if (intent.isSuccess) {
          enemy.armor += intent.finalValue;
          _addLog(
            "${enemy.name} assumes a defensive stance (+${intent.finalValue} Armor).",
          );
        } else {
          _addLog("${enemy.name} tried to defend but failed.");

          // Optional: Emit failed defense visual?
          // For now, let's keep it simple as log only for failure, or add visual later.
        }
        intent.isApplied = true; // Mark as applied so we don't do it again
      }
      // Add other pre-emptive intent types here if needed (e.g., Buffs)
    }

    isPlayerTurn = true;
    turnCount++;
    notifyListeners();
  }

  void _addLog(String log) {
    _logManager.addLog(log);
    notifyListeners();
  }

  /// Check Status Effects at Start of Turn
  bool _processStartTurnEffects(Character character) {
    final result = CombatCalculator.processStartTurnEffects(character);

    int totalBleed = result['bleedDamage'];
    bool isStunned = result['isStunned'];

    // 1. Bleed Damage
    if (totalBleed > 0) {
      character.hp -= totalBleed;
      if (character.hp < 0) character.hp = 0;
      _addLog("${character.name} takes $totalBleed bleed damage!");

      // Emit DamageEvent for bleed
      _damageEventController.sink.add(
        DamageEvent(
          damage: totalBleed,
          target: (character == player)
              ? DamageTarget.player
              : DamageTarget.enemy,
          type: DamageType.bleed,
        ),
      );
    }

    // 2. Stun Check
    if (isStunned) {
      _addLog("${character.name} is stunned!");
    }

    return !isStunned;
  }

  // --- Turn Management Phases ---

  // Phase 1: Enemy Action Phase
  Future<void> _startEnemyTurn() async {
    _turnTransactionId++; // Start of Enemy Turn Phase
    if (!isPlayerTurn && (player.isDead || enemy.isDead)) return;

    _addLog("Enemy's turn...");

    // Armor decay (Enemy)
    if (enemy.armor > 0) {
      enemy.armor = (enemy.armor * GameConfig.armorDecayRate).toInt();
      _addLog("Enemy's armor decayed to ${enemy.armor}.");
    }

    // Process Start-of-Turn Effects
    bool canAct = _processStartTurnEffects(enemy);

    if (enemy.isDead) {
      _onVictory();
      return;
    }

    if (canAct && currentEnemyIntent != null) {
      final intent = currentEnemyIntent!;

      if (intent.type == EnemyActionType.defend) {
        // Defensive Action (Pre-applied at start of Player Turn)
        // Just show a log or maintain stance visual
        _addLog("${enemy.name} maintains defensive stance.");

        // IMPORTANT: We still need to end the turn sequence properly.
        // Since no animation is needed (or a very short one), we can just delay slightly.
        int tid = _turnTransactionId;
        Future.delayed(const Duration(milliseconds: 500), () {
          if (tid != _turnTransactionId) return;
          _endEnemyTurn();
        });
        return;
      } else {
        // Attack Action (Animating)
        if (intent.isSuccess) {
          final event = EffectEvent(
            id:
                DateTime.now().millisecondsSinceEpoch.toString() +
                Random().nextInt(1000).toString(),
            type: ActionType.attack,
            risk: intent.risk,
            target: EffectTarget.player,
            feedbackType: null,
            attacker: enemy,
            targetEntity: player,
            damageValue: intent.finalValue,
            isSuccess: true,
          );
          _effectEventController.sink.add(event);
          // UI monitors event -> animates -> calls handleImpact -> _endEnemyTurn
          return;
        } else {
          // Missed Attack
          _addLog("Enemy's ${intent.risk.name} attack missed!");
          final event = EffectEvent(
            id:
                DateTime.now().millisecondsSinceEpoch.toString() +
                Random().nextInt(1000).toString(),
            type: ActionType.attack,
            risk: intent.risk,
            target: EffectTarget.player,
            feedbackType: BattleFeedbackType.miss,
            attacker: enemy,
            targetEntity: player,
            isSuccess: false,
          );
          _effectEventController.sink.add(event);
          return;
        }
      }
    } else if (!canAct) {
      _addLog("Enemy is stunned and cannot act!");
      int tid = _turnTransactionId;
      Future.delayed(const Duration(milliseconds: 500), () {
        if (tid != _turnTransactionId) return;
        _endEnemyTurn();
      });
    } else {
      _addLog("Enemy did nothing.");
      int tid = _turnTransactionId;
      Future.delayed(const Duration(milliseconds: 500), () {
        if (tid != _turnTransactionId) return;
        _endEnemyTurn();
      });
    }
  }

  // Phase 2: End Enemy Turn & Generate Next Intent
  void _endEnemyTurn() {
    if (player.isDead) return; // Game Over check

    // Generate NEXT intent
    _generateEnemyIntent();

    _processMiddleTurn();
  }

  // Phase 3: Middle Turn (Apply Defense Effects)
  Future<void> _processMiddleTurn() async {
    // Phase 3 Middle Turn - Reduced functionality as Defense is now Phase 1.
    int tid = _turnTransactionId;
    // await Future.delayed(const Duration(milliseconds: 200)); // Removed for faster turn transition
    if (tid != _turnTransactionId) return;

    _startPlayerTurn();
  }

  void _onVictory() {
    // Calculate Gold Reward
    // Base 10 + (Stage * 5) + Random variance
    final random = Random();
    int goldReward =
        GameConfig.baseGoldReward +
        (stage * GameConfig.goldRewardPerStage) +
        random.nextInt(GameConfig.goldRewardVariance);

    player.gold += goldReward;
    _lastGoldReward = goldReward; // Store for UI display
    _addLog("Enemy defeated! Gained $goldReward Gold.");
    _addLog("Choose a reward.");

    ItemTier currentTier = ItemTier.tier1;
    if (stage > GameConfig.tier2StageMax)
      currentTier = ItemTier.tier3;
    else if (stage > GameConfig.tier1StageMax)
      currentTier = ItemTier.tier2;

    rewardOptions = [];

    bool isElite = currentStage.type == StageType.elite;
    bool isTier1 = currentTier == ItemTier.tier1;

    // Get 3 distinct items
    for (int i = 0; i < 3; i++) {
      ItemRarity? minRarity;
      ItemRarity? maxRarity;

      // 1. Elite Reward Logic (First Item only)
      if (isElite && i == 0) {
        if (isTier1) {
          // Tier 1 Elite: Guaranteed Rare
          minRarity = ItemRarity.rare;
          maxRarity = ItemRarity
              .rare; // Or allow higher? Request said "Guaranteed Rare 1 drop". Let's fix to Rare.
        } else {
          // Tier 2/3 Elite: Guaranteed Legendary
          minRarity = ItemRarity.legendary;
          // maxRarity = ItemRarity.legendary; // Optional, but let's allow Unique too if weights permit, or fix to Legendary. Request said "Guaranteed Legendary".
        }
      }
      // 2. Standard Reward Logic (Others)
      else {
        if (isTier1) {
          // Tier 1 Normal/Other Rewards: Max Magic (No Rare+)
          maxRarity = ItemRarity.magic;
        }
        // Tier 2/3 Normal: No extra restrictions
      }

      ItemTemplate? item = ItemTable.getRandomItem(
        tier: currentTier,
        minRarity: minRarity,
        maxRarity: maxRarity,
      );

      if (item != null) {
        rewardOptions.add(item.createItem(stage: stage));
      }
    }

    // Add "None" (Skip) Option
    // We can represent "None" as a null or a special Item.
    // Using a special Item with ID "reward_skip" is safer for List<Item>.
    rewardOptions.add(
      Item(
        id: "reward_skip",
        name: "Skip Reward",
        description: "Take nothing and move on.",
        atkBonus: 0,
        hpBonus: 0,
        slot: EquipmentSlot.accessory,
      ),
    );

    showRewardPopup = true;
    notifyListeners();
  }

  bool selectReward(Item item) {
    if (item.id == "reward_skip") {
      _addLog("Skipped reward.");
      _completeStage();
      return true;
    } else {
      bool added = player.addToInventory(item);
      if (added) {
        _addLog("Added ${item.name} to inventory.");
        _completeStage();
        return true;
      } else {
        _addLog("Inventory is full! Could not take ${item.name}.");
        return false;
      }
    }
  }

  void _completeStage() {
    // Heal player after selecting reward
    int healAmount = GameMath.floor(
      player.totalMaxHp * GameConfig.stageHealRatio,
    );
    player.heal(healAmount);
    _addLog("Stage Cleared! Recovered $healAmount HP.");

    stage++;
    showRewardPopup = false;

    _prepareNextStage();

    // Log moved to _prepareNextStage

    // isPlayerTurn = true; // Handled in _prepareNextStage for battles
    notifyListeners();
  }

  void equipItem(Item item) {
    if (player.equip(item)) {
      _addLog("Equipped ${item.name}.");
    } else {
      _addLog(
        "Failed to equip ${item.name}.",
      ); // Should not happen if logic is correct
    }
    notifyListeners();
  }

  void unequipItem(Item item) {
    if (player.unequip(item)) {
      _addLog("Unequipped ${item.name}.");
    } else {
      _addLog("Failed to unequip ${item.name} (Inventory might be full).");
    }
    notifyListeners();
  }

  void discardItem(Item item) {
    if (player.inventory.remove(item)) {
      _addLog("Discarded ${item.name}.");
      notifyListeners();
    }
  }

  void sellItem(Item item) {
    if (player.inventory.remove(item)) {
      int sellPrice = GameMath.floor(
        item.price * GameConfig.sellPriceMultiplier,
      );
      player.gold += sellPrice;
      _addLog("Sold ${item.name} for $sellPrice G.");
      notifyListeners();
    }
  }

  /// Proceed to next stage from non-battle stages (Shop, Rest)
  void proceedToNextStage() {
    stage++;
    _prepareNextStage();
  }

  void _generateEnemyIntent() {
    if (enemy.isDead) {
      currentEnemyIntent = null;
      return;
    }

    final random = Random();

    // Decide Action Type
    bool canDefend = enemy.baseDefense > 0;
    if (enemy.hasStatus(StatusEffectType.defenseForbidden)) {
      canDefend = false;
    }
    bool isAttack = true;

    if (canDefend) {
      isAttack = random.nextDouble() < BattleConfig.enemyAttackChance;
    } else {
      isAttack = true;
    }

    // Decide Risk Level
    RiskLevel risk = RiskLevel.values[random.nextInt(RiskLevel.values.length)];

    CombatResult result;
    if (isAttack) {
      result = CombatCalculator.calculateActionOutcome(
        actionType: ActionType.attack,
        risk: risk,
        luck: enemy.totalLuck,
        baseValue: enemy.totalAtk,
      );

      currentEnemyIntent = EnemyIntent(
        type: EnemyActionType.attack,
        value: result.value, // Damage value from CombatCalculator
        risk: risk,
        description: "${result.value} (${risk.name})",
        isSuccess: result.success,
        finalValue: result.value,
      );
    } else {
      result = CombatCalculator.calculateActionOutcome(
        actionType: ActionType.defend,
        risk: risk,
        luck: enemy.totalLuck,
        baseValue: enemy.totalDefense,
      );

      currentEnemyIntent = EnemyIntent(
        type: EnemyActionType.defend,
        value: result.value, // Armor value from CombatCalculator
        risk: risk,
        description: "${result.value} (${risk.name})",
        isSuccess: result.success,
        finalValue: result.value,
      );
    }
    notifyListeners();
  }

  /// Applies the effects of the enemy's intent (specifically Defense)
  /// This should be called just before the Player's turn starts.
  void _applyEnemyIntentEffects() {
    // No pre-emptive effects in Standard Turn-Based model.
    // Logic cleared.
  }

  // New public method to be called by UI at impact moment
  void handleImpact(EffectEvent event) {
    if (event.isVisualOnly) {
      // Logic Skipped. Just log if needed, but usually logging is done at event creation.
      // We do NOT process damage or armor here.
      notifyListeners();
      return;
    }

    if ((event.isSuccess == false || event.feedbackType != null) &&
        event.type != ActionType.defend) {
      // If it's a miss/fail/feedback, just log and return
      // Logging and feedback text should already be handled when event created
      notifyListeners(); // Ensure UI updates for log

      // Even on failure, proceed to end turn logic
      if (event.attacker == player) {
        _endPlayerTurn();
      } else if (event.attacker == enemy) {
        // Special Case: Phase 1 relies on manual timer. Phase 3 relies on _processMiddleTurn sequence.
        _endEnemyTurn();
      }
      return;
    }

    // Special Case: Enemy Defense (Phase 3 & Phase 1)
    // - Phase 3 Defense: Logic applied in _applyEnemyIntentEffects. Event is Visual Only.
    // - Phase 1 Defense: Logic applied in _startEnemyTurn (if we add it there) or here?
    //   Wait, Phase 1 Defense is distinct.
    //   However, currently Phase 1 Defense also uses _effectEventController.sink.add(event).
    //   BUT Phase 1 Defense Logic is NOT applied in _startEnemyTurn yet (it just emits event).
    //   So Phase 1 Defense SHOULD go through _processAttackImpact?
    //   NO, because Phase 1 Defense uses the same ActionType.defend.

    // Let's look at _startEnemyTurn for Phase 1 Defense:
    // It emits event with armorGained. It does NOT increase armor directly.
    // So for Phase 1, we NEED handleImpact -> _processAttackImpact.

    // Let's look at _applyEnemyIntentEffects for Phase 3 Defense:
    // It increases armor DIRECTLY: "enemy.armor += intent.finalValue;"
    // AND it emits event.

    // This discrepancy is the root cause.
    // We should standardize.

    // DECISION: Phase 3 Defense event should be flagged or handled as visual-only.
    // Since we can't easily add flags to EffectEvent without changing other files,
    // let's rely on the context.

    // Actually, simply removing the direct armor application in _applyEnemyIntentEffects
    // and letting handleImpact do it is cleaner?
    // NO, because Phase 3 needs armor applied BEFORE Player Turn starts, independent of UI speed.
    // And _processMiddleTurn relies on the logic sequence.

    // So, we MUST block handleImpact for Phase 3 Defense.
    // Phase 1 Defense (Rare, usually Attack) needs to work too.

    // BUT wait, _startEnemyTurn (Phase 1) code:
    // if (intent.type == EnemyActionType.defend) { ... sink.add(event); ... }
    // It does NOT apply armor. So Phase 1 relies on handleImpact.

    // PROBLEM: handleImpact cannot distinguish Phase 1 vs Phase 3 event easily.

    // FIX: Update _startEnemyTurn (Phase 1) to ALSO apply armor directly and make the event visual-only.
    // Then we can globally block Enemy Defend in handleImpact.

    // REMOVED: Blocking Enemy Defend. Now we want to process it.
    // if (event.attacker == enemy && event.type == ActionType.defend) {
    //   return;
    // }

    // Only process actual attack or defend impacts here
    _processAttackImpact(event);

    // After processing impact, proceed to end turn logic
    if (event.triggersTurnChange) {
      if (event.attacker == player) {
        _endPlayerTurn();
      } else if (event.attacker == enemy) {
        _endEnemyTurn();
      }
    }
  }

  // Refactored common attack impact logic
  void _processAttackImpact(EffectEvent event) {
    final attacker = event.attacker!;
    final target = event.targetEntity!;

    // Attack type needs detailed damage calculation
    if (event.type == ActionType.attack) {
      int incomingDamage = event.damageValue!;

      // Calculate Damage to HP using CombatCalculator
      int damageToHp = CombatCalculator.calculateDamageToHp(
        incomingDamage: incomingDamage,
        currentArmor: target.armor,
        isVulnerable: target.hasStatus(StatusEffectType.vulnerable),
      );

      // Calculate Remaining Armor
      int remainingArmor = CombatCalculator.calculateRemainingArmor(
        incomingDamage: incomingDamage,
        currentArmor: target.armor,
        isVulnerable: target.hasStatus(StatusEffectType.vulnerable),
      );

      // Log details
      if (target.armor > 0) {
        int absorbed = target.armor - remainingArmor;
        if (damageToHp == 0) {
          _addLog("${target.name}'s armor absorbed all damage.");
        } else {
          _addLog("${target.name}'s armor absorbed $absorbed damage.");
        }
      }
      target.armor = remainingArmor;

      if (damageToHp > 0) {
        target.hp -= damageToHp;
        if (target.hp < 0) target.hp = 0;

        _damageEventController.sink.add(
          DamageEvent(
            damage: damageToHp,
            target: (target == player)
                ? DamageTarget.player
                : DamageTarget.enemy,
            type: target.hasStatus(StatusEffectType.vulnerable)
                ? DamageType.vulnerable
                : DamageType.normal,
          ),
        );
        _addLog("${attacker.name} dealt $damageToHp damage to ${target.name}.");
      } else {
        _addLog("${attacker.name}'s attack was fully blocked by armor.");
      }

      // Try applying status effects
      _tryApplyStatusEffects(attacker, target);
    } else if (event.type == ActionType.defend) {
      // Defense Impact is immediate (no anim delay from UI)
      if (event.isSuccess!) {
        // Check success again for clarity
        int armorGained = event.armorGained!;
        target.armor += armorGained;
        _addLog("${target.name} gained $armorGained armor.");
      } else {
        // Failed Defense
        _addLog("${target.name}'s defense failed!");
      }
    }

    // Check for death after impact
    if (target.isDead) {
      if (target == player) {
        _onDefeat();
      } else {
        _onVictory();
      }
    }
    notifyListeners();
  }

  /// Tries to apply status effects from attacker's equipment to the target.
  void _tryApplyStatusEffects(Character attacker, Character target) {
    List<StatusEffect> effectsToApply = CombatCalculator.getAppliedEffects(
      attacker,
    );

    for (var effect in effectsToApply) {
      target.addStatusEffect(effect);
      _addLog("Applied ${effect.type.name} to ${target.name}!");
    }
  }
}
