import { SkillProcessor } from './SkillProcessor.js';
import { CombatAI } from './CombatAI.js';
import { RNG } from '../utils/RNG.js';

const MAX_LANES = 4;
const MAX_HAND = 3;

export class CombatEngine {
  constructor(playerDeck, opponentDeck, cardManager, seed) {
    this.cardManager = cardManager;
    this.skillProcessor = new SkillProcessor();
    this.rng = new RNG(seed || Date.now());

    // Player side
    this.playerCommander = cardManager.createInstance(playerDeck.commander);
    this.playerDeck = playerDeck.cards.map(id => cardManager.createInstance(id));
    this.rng.shuffle(this.playerDeck);
    this.playerDeckIdx = 0;
    this.playerHand = [];
    this.playerLanes = []; // active cards in lanes

    // Opponent side (AI)
    this.ai = new CombatAI(opponentDeck, cardManager, seed ? seed + 1 : Date.now() + 1);
    this.opponentCommander = this.ai.commander;
    this.opponentLanes = [];

    this.turn = 0;
    this.log = [];
    this.winner = null; // 'player' | 'opponent' | null
    this.events = []; // events for animation: [{type, data}]
    this.playerGoesFirst = true; // alternates each turn
  }

  _log(msg) {
    this.log.push(`Turn ${this.turn}: ${msg}`);
  }

  // Draw up to MAX_HAND cards
  _drawPhase() {
    while (this.playerHand.length < MAX_HAND && this.playerDeckIdx < this.playerDeck.length) {
      this.playerHand.push(this.playerDeck[this.playerDeckIdx++]);
    }
    this.ai.drawCards(Math.max(0, MAX_HAND - this.ai.hand.length));
  }

  // Deploy one card from hand into next open lane
  _deployPhase() {
    if (this.playerHand.length > 0 && this.playerLanes.length < MAX_LANES) {
      const card = this.playerHand.shift();
      this.playerLanes.push(card);
      this._log(`Player deploys ${card.name}`);
      this.events.push({ type: 'deploy', side: 'player', card });
    }
    const aiCard = this.ai.getNextCard();
    if (aiCard && this.opponentLanes.length < MAX_LANES) {
      this.opponentLanes.push(aiCard);
      this._log(`Opponent deploys ${aiCard.name}`);
      this.events.push({ type: 'deploy', side: 'opponent', card: aiCard });
    }
  }

  // Tick delay counters
  _activationPhase() {
    const tick = cards => {
      for (const c of cards) {
        if (c.currentDelay > 0) c.currentDelay--;
      }
    };
    tick(this.playerLanes);
    tick(this.opponentLanes);
    tick(this.playerHand);
    tick(this.ai.hand);
  }

  // Apply rupture damage to all cards
  _rupturePhase() {
    const applyRupture = cards => {
      for (const c of cards) {
        if (c.ruptureStacks > 0) {
          c.currentHP -= c.ruptureStacks;
          this._log(`${c.name} takes ${c.ruptureStacks} rupture damage`);
        }
      }
    };
    applyRupture(this.playerLanes);
    applyRupture(this.opponentLanes);
  }

  // Tick jam timers
  _jamPhase() {
    const tickJam = cards => {
      for (const c of cards) {
        if (c.jamTurns > 0) c.jamTurns--;
      }
    };
    tickJam(this.playerLanes);
    tickJam(this.opponentLanes);
  }

  // Fire on_turn_start skills for commanders
  _commanderSkillPhase() {
    if (this.playerCommander.currentHP > 0) {
      for (const s of this.playerCommander.skills) {
        if (s.trigger === 'on_turn_start') {
          this.skillProcessor.process(s, this.playerCommander, null,
            [this.playerCommander, ...this.playerLanes],
            [this.opponentCommander, ...this.opponentLanes],
            { enemyCommander: this.opponentCommander, cardManager: this.cardManager }
          );
        }
      }
    }
    if (this.opponentCommander.currentHP > 0) {
      for (const s of this.opponentCommander.skills) {
        if (s.trigger === 'on_turn_start') {
          this.skillProcessor.process(s, this.opponentCommander, null,
            [this.opponentCommander, ...this.opponentLanes],
            [this.playerCommander, ...this.playerLanes],
            { enemyCommander: this.playerCommander, cardManager: this.cardManager }
          );
        }
      }
    }
  }

  // Cards attack across lanes
  _attackPhase() {
    // Player cards attack opponent lanes / commander
    for (let i = 0; i < this.playerLanes.length; i++) {
      const attacker = this.playerLanes[i];
      if (attacker.currentHP <= 0 || attacker.currentDelay > 0 || attacker.jamTurns > 0) continue;

      const target = this.opponentLanes[i] || null;
      this._performAttack(attacker, target, this.opponentCommander,
        this.playerLanes, this.opponentLanes, 'player');
    }

    // Opponent cards attack player lanes / commander
    for (let i = 0; i < this.opponentLanes.length; i++) {
      const attacker = this.opponentLanes[i];
      if (attacker.currentHP <= 0 || attacker.currentDelay > 0 || attacker.jamTurns > 0) continue;

      const target = this.playerLanes[i] || null;
      this._performAttack(attacker, target, this.playerCommander,
        this.opponentLanes, this.playerLanes, 'opponent');
    }
  }

  _performAttack(attacker, target, enemyCommander, alliedLanes, enemyLanes, side) {
    const context = {
      enemyCommander,
      cardManager: this.cardManager,
      attackingCard: attacker
    };

    // Pre-attack skills (pierce, flurry, valor, legion)
    for (const s of attacker.skills) {
      if (['pierce', 'flurry', 'valor', 'legion'].includes(s.name) &&
          ['on_attack', 'passive'].includes(s.trigger)) {
        this.skillProcessor.process(s, attacker, target,
          alliedLanes.filter(c => c.currentHP > 0),
          enemyLanes.filter(c => c.currentHP > 0), context);
      }
    }

    const extraAttacks = (context.extraAttacks || 0) + 1;
    for (let a = 0; a < extraAttacks; a++) {
      const currentTarget = target || null;
      if (currentTarget) {
        // Attack the lane card
        const pierce = context.pierceBonus || 0;
        const effectiveArmor = Math.max(0, currentTarget.currentArmor - pierce);
        let dmg = Math.max(0, attacker.currentAttack - effectiveArmor);
        // armored skill on defender
        const armorSkill = currentTarget.skills.find(s => s.name === 'armored');
        if (armorSkill) dmg = Math.max(0, dmg - armorSkill.value);
        currentTarget.currentHP -= dmg;
        this._log(`${attacker.name} attacks ${currentTarget.name} for ${dmg}`);
        this.events.push({ type: 'attack', attacker, defender: currentTarget, damage: dmg, side });

        // on_attack skills
        for (const s of attacker.skills) {
          if (s.trigger === 'on_attack') {
            this.skillProcessor.process(s, attacker, currentTarget,
              alliedLanes.filter(c => c.currentHP > 0),
              enemyLanes.filter(c => c.currentHP > 0),
              { ...context, trigger: 'on_attack', damageDealt: dmg });
          }
        }

        // Emit berserk event if the attacker has berserk and dealt damage
        if (dmg > 0) {
          for (const s of attacker.skills) {
            if (s.name === 'berserk') {
              this.events.push({ type: 'berserk', card: attacker, gain: s.value });
            }
          }
        }

        // on_defend / counter skills on defender (if still alive)
        if (currentTarget.currentHP > 0) {
          for (const s of currentTarget.skills) {
            if (['counter'].includes(s.name)) {
              this.skillProcessor.process(s, currentTarget, attacker,
                enemyLanes.filter(c => c.currentHP > 0),
                alliedLanes.filter(c => c.currentHP > 0),
                { ...context, attackingCard: attacker, trigger: 'on_defend' });
            }
          }
        }

        // Check if defender died
        if (currentTarget.currentHP <= 0) {
          this._log(`${currentTarget.name} is destroyed`);
          this.events.push({ type: 'death', card: currentTarget, side: side === 'player' ? 'opponent' : 'player' });
        }
      } else {
        // Attack commander directly
        const pierce = context.pierceBonus || 0;
        const effectiveArmor = Math.max(0, enemyCommander.currentArmor - pierce);
        const dmg = Math.max(0, attacker.currentAttack - effectiveArmor);
        enemyCommander.currentHP -= dmg;
        this._log(`${attacker.name} attacks ${enemyCommander.name} for ${dmg}`);
        this.events.push({ type: 'attack', attacker, defender: enemyCommander, damage: dmg, side });

        // on_attack skills that hit commander directly too
        for (const s of attacker.skills) {
          if (s.trigger === 'on_attack' && ['siege', 'strike'].includes(s.name)) {
            this.skillProcessor.process(s, attacker, enemyCommander,
              alliedLanes, enemyLanes, { ...context, trigger: 'on_attack' });
          }
        }
      }
    }
  }

  // Remove dead cards, compact lanes
  _deathCheck() {
    this.playerLanes = this.playerLanes.filter(c => c.currentHP > 0);
    this.opponentLanes = this.opponentLanes.filter(c => c.currentHP > 0);
  }

  _winCheck() {
    if (this.playerCommander.currentHP <= 0) {
      this.winner = 'opponent';
      this._log('Player commander destroyed — Opponent wins!');
      return true;
    }
    if (this.opponentCommander.currentHP <= 0) {
      this.winner = 'player';
      this._log('Opponent commander destroyed — Player wins!');
      return true;
    }
    // Check if both sides are out of cards
    if (this.playerDeckIdx >= this.playerDeck.length &&
        this.playerHand.length === 0 && this.playerLanes.length === 0) {
      this.winner = 'opponent';
      this._log('Player ran out of cards — Opponent wins!');
      return true;
    }
    return false;
  }

  // Phase 1: draw cards, AI deploys (only when opponent goes first), return player's hand
  beginTurn() {
    if (this.winner) return { hand: [], canDeploy: false, playerGoesFirst: this.playerGoesFirst };
    this.events = [];
    this.turn++;
    this._log(`--- Turn ${this.turn} ---`);
    this._drawPhase();
    // AI deploys immediately only on opponent-first turns
    if (!this.playerGoesFirst) {
      const aiCard = this.ai.getNextCard();
      if (aiCard && this.opponentLanes.length < MAX_LANES) {
        this.opponentLanes.push(aiCard);
        this._log(`Opponent deploys ${aiCard.name}`);
        this.events.push({ type: 'deploy', side: 'opponent', card: aiCard });
      }
    }
    const canDeploy = this.playerHand.length > 0 && this.playerLanes.length < MAX_LANES;
    return { hand: [...this.playerHand], canDeploy, playerGoesFirst: this.playerGoesFirst };
  }

  // Phase 2: deploy chosen card (or null to pass), then resolve the turn
  commitPlayerDeploy(chosenCard) {
    if (chosenCard && this.playerLanes.length < MAX_LANES) {
      const idx = this.playerHand.findIndex(c => c.instanceId === chosenCard.instanceId);
      if (idx !== -1) {
        this.playerHand.splice(idx, 1);
        this.playerLanes.push(chosenCard);
        this._log(`Player deploys ${chosenCard.name}`);
        this.events.push({ type: 'deploy', side: 'player', card: chosenCard });
      }
    }
    this._activationPhase();
    this._commanderSkillPhase();
    this._rupturePhase();
    this._jamPhase();
    this._attackPhase();
    this._deathCheck();
    this._winCheck();
    return [...this.events];
  }

  // ── Step-by-step combat API (used by animated battle scene) ──────────────

  // Deploy the chosen card and run all pre-attack phases.
  // Does NOT resolve attacks yet — call processNextAttack() for each one.
  beginCommit(chosenCard) {
    this.events = [];
    if (chosenCard && this.playerLanes.length < MAX_LANES) {
      const idx = this.playerHand.findIndex(c => c.instanceId === chosenCard.instanceId);
      if (idx !== -1) {
        this.playerHand.splice(idx, 1);
        this.playerLanes.push(chosenCard);
        this._log(`Player deploys ${chosenCard.name}`);
        this.events.push({ type: 'deploy', side: 'player', card: chosenCard });
      }
    }
    // On player-first turns, AI deploys after the player
    if (this.playerGoesFirst) {
      const aiCard = this.ai.getNextCard();
      if (aiCard && this.opponentLanes.length < MAX_LANES) {
        this.opponentLanes.push(aiCard);
        this._log(`Opponent deploys ${aiCard.name}`);
        this.events.push({ type: 'deploy', side: 'opponent', card: aiCard });
      }
    }
    this._activationPhase();
    this._commanderSkillPhase();
    this._rupturePhase();
    this._jamPhase();
    this._buildPendingAttacks();
    this.playerGoesFirst = !this.playerGoesFirst; // flip initiative for next turn
    return [...this.events];
  }

  // Build the ordered list of attacks for this turn (called by beginCommit).
  // Attack order respects the current initiative (playerGoesFirst, before it is flipped).
  _buildPendingAttacks() {
    this._pendingAttacks = [];
    const addAttacks = (attackerLanes, enemyLanes, enemyCommander, alliedLanes, side) => {
      for (let i = 0; i < attackerLanes.length; i++) {
        const attacker = attackerLanes[i];
        if (attacker.currentHP <= 0 || attacker.currentDelay > 0 || attacker.jamTurns > 0) continue;
        this._pendingAttacks.push({
          attacker,
          target: enemyLanes[i] || null,
          alliedLanes,
          enemyLanes,
          enemyCommander,
          side
        });
      }
    };
    if (this.playerGoesFirst) {
      addAttacks(this.playerLanes, this.opponentLanes, this.opponentCommander, this.playerLanes, 'player');
      addAttacks(this.opponentLanes, this.playerLanes, this.playerCommander, this.opponentLanes, 'opponent');
    } else {
      addAttacks(this.opponentLanes, this.playerLanes, this.playerCommander, this.opponentLanes, 'opponent');
      addAttacks(this.playerLanes, this.opponentLanes, this.opponentCommander, this.playerLanes, 'player');
    }
  }

  // Execute exactly one pending attack and return its events.
  // Returns [] if the attacker died from a previous counter-attack this turn.
  processNextAttack() {
    if (!this._pendingAttacks || this._pendingAttacks.length === 0) return [];
    const pending = this._pendingAttacks.shift();
    if (pending.attacker.currentHP <= 0) return this.processNextAttack();
    this.events = [];
    this._performAttack(
      pending.attacker, pending.target, pending.enemyCommander,
      pending.alliedLanes, pending.enemyLanes, pending.side
    );
    return [...this.events];
  }

  hasPendingAttacks() {
    return !!(this._pendingAttacks && this._pendingAttacks.length > 0);
  }

  // Run death-check and win-check after all attacks have been animated.
  finalizeCommit() {
    this.events = [];
    this._deathCheck();
    this._winCheck();
    return [...this.events];
  }

  // Run one full turn, return events (used by runToCompletion)
  stepTurn() {
    if (this.winner) return this.events;
    this.events = [];
    this.turn++;
    this._log(`--- Turn ${this.turn} ---`);
    this._drawPhase();
    this._deployPhase();
    this._activationPhase();
    this._commanderSkillPhase();
    this._rupturePhase();
    this._jamPhase();
    this._attackPhase();
    this._deathCheck();
    this._winCheck();
    return [...this.events];
  }

  // Run entire battle to completion (for testing)
  runToCompletion(maxTurns = 200) {
    while (!this.winner && this.turn < maxTurns) {
      this.stepTurn();
    }
    if (!this.winner) {
      this.winner = 'opponent'; // timeout = player loses
      this._log('Turn limit reached — Opponent wins');
    }
    return this.winner;
  }

  getState() {
    return {
      turn: this.turn,
      winner: this.winner,
      player: {
        commander: this.playerCommander,
        lanes: this.playerLanes,
        hand: this.playerHand,
        deckRemaining: this.playerDeck.length - this.playerDeckIdx
      },
      opponent: {
        commander: this.opponentCommander,
        lanes: this.opponentLanes,
        hand: this.ai.hand,
        deckRemaining: this.ai.deckCards.length - this.ai.deckIndex
      }
    };
  }
}
