'use strict';

/**
 * Sistema modular de preparação e resolução de ataques.
 *
 * Responsabilidades deste ficheiro:
 * - apresentar o planeador de ataque por vagas;
 * - permitir escolher flanco esquerdo, centro e flanco direito;
 * - distribuir tropas por vaga e por flanco;
 * - validar e enviar a ordem de ataque;
 * - resolver batalhas criadas pelo planeador sem alterar os restantes sistemas.
 */
(() => {
  const PLAN_VERSION = 2;
  const WAVE_COUNT = 4;
  const FLANKS = Object.freeze([
    { id: 'left', name: 'Flanco esquerdo', icon: '◀' },
    { id: 'center', name: 'Centro', icon: '◆' },
    { id: 'right', name: 'Flanco direito', icon: '▶' }
  ]);

  let draft = null;

  function emptyTroops(troopMeta) {
    return Object.fromEntries(Object.keys(troopMeta).map(type => [type, 0]));
  }

  function createDraft(target, context) {
    const troopMeta = context.getTroopsMeta();
    const result = {
      targetId: target.id,
      targetType: target.targetType || 'falken',
      targetSnapshot: { ...target, loot: { ...(target.loot || {}) } },
      activeWave: 0,
      context,
      waves: Array.from({ length: WAVE_COUNT }, (_, waveIndex) => ({
        number: waveIndex + 1,
        flanks: Object.fromEntries(FLANKS.map(flank => [flank.id, {
          enabled: waveIndex === 0,
          troops: emptyTroops(troopMeta)
        }]))
      }))
    };
    globalThis.ReinosAttackTools?.initializeDraft?.(result, context);
    return result;
  }

  function getTarget() {
    if (!draft) return null;
    const dynamic = draft.context?.getAttackTarget?.(draft.targetId, draft.targetType);
    if (dynamic) return dynamic;
    if (draft.targetType === 'falken') {
      return draft.context?.getProfile?.()?.targets?.find(target => target.id === draft.targetId) || draft.targetSnapshot || null;
    }
    return draft.targetSnapshot || null;
  }

  function clonePlanWaves(waves) {
    return waves.map(wave => ({
      number: wave.number,
      flanks: Object.fromEntries(FLANKS.map(flank => {
        const source = wave.flanks[flank.id];
        return [flank.id, {
          enabled: Boolean(source?.enabled),
          troops: Object.fromEntries(Object.entries(source?.troops || {}).map(([type, count]) => [type, Math.max(0, Math.floor(Number(count) || 0))])),
          tools: globalThis.ReinosAttackTools?.cloneTools?.(source?.tools) || {}
        }];
      }))
    }));
  }

  function assignedByType(type, excluded = null) {
    if (!draft) return 0;
    let total = 0;
    draft.waves.forEach((wave, waveIndex) => {
      FLANKS.forEach(flank => {
        if (excluded && excluded.waveIndex === waveIndex && excluded.flankId === flank.id) return;
        total += Math.max(0, Math.floor(Number(wave.flanks[flank.id]?.troops?.[type]) || 0));
      });
    });
    return total;
  }

  function totalAssigned() {
    if (!draft) return 0;
    const troopMeta = draft.context.getTroopsMeta();
    return Object.keys(troopMeta).reduce((sum, type) => sum + assignedByType(type), 0);
  }

  function waveTotal(waveIndex) {
    const wave = draft?.waves?.[waveIndex];
    if (!wave) return 0;
    return FLANKS.reduce((sum, flank) => sum + Object.values(wave.flanks[flank.id]?.troops || {}).reduce((a, b) => a + (Number(b) || 0), 0), 0);
  }

  function flankTotal(waveIndex, flankId) {
    const flank = draft?.waves?.[waveIndex]?.flanks?.[flankId];
    return Object.values(flank?.troops || {}).reduce((sum, count) => sum + (Number(count) || 0), 0);
  }

  function attackStrength() {
    if (!draft) return 0;
    const troopMeta = draft.context.getTroopsMeta();
    return Object.keys(troopMeta).reduce((sum, type) => sum + assignedByType(type) * (Number(troopMeta[type].attack) || 0), 0);
  }

  function selectedFlanks() {
    const result = new Set();
    draft?.waves?.forEach(wave => {
      FLANKS.forEach(flank => {
        if (wave.flanks[flank.id]?.enabled && Object.values(wave.flanks[flank.id].troops || {}).some(value => Number(value) > 0)) result.add(flank.id);
      });
    });
    return result;
  }

  function troopRow(type, troop, flankId, flank, waveIndex, context) {
    const disabled = !flank.enabled;
    const value = Math.max(0, Math.floor(Number(flank.troops[type]) || 0));
    return `<label class="attack-troop-row ${disabled ? 'is-disabled' : ''}">
      <span class="attack-troop-name"><b>${troop.icon}</b><span>${context.escapeHtml(troop.name)}</span></span>
      <input class="attack-plan-input" data-wave="${waveIndex}" data-flank="${flankId}" data-type="${type}" type="number" min="0" step="1" value="${value}" ${disabled ? 'disabled' : ''} />
    </label>`;
  }

  function flankCard(flankDef, waveIndex, context) {
    const troopMeta = context.getTroopsMeta();
    const flank = draft.waves[waveIndex].flanks[flankDef.id];
    const rows = Object.entries(troopMeta).map(([type, troop]) => troopRow(type, troop, flankDef.id, flank, waveIndex, context)).join('');
    const tools = globalThis.ReinosAttackTools?.renderFlankTools?.(waveIndex, flankDef.id, context) || '';
    return `<section class="attack-flank-card ${flank.enabled ? 'is-enabled' : 'is-disabled'}" data-flank-card="${flankDef.id}">
      <button class="attack-flank-toggle" data-action="attack-plan-toggle-flank" data-wave="${waveIndex}" data-flank="${flankDef.id}" aria-pressed="${flank.enabled}">
        <span>${flank.enabled ? '✓' : '○'}</span>
        <b>${flankDef.icon} ${flankDef.name}</b>
      </button>
      <div class="attack-flank-total"><span>Tropas nesta zona</span><strong data-flank-total="${flankDef.id}">${context.format(flankTotal(waveIndex, flankDef.id))}</strong></div>
      <div class="attack-troop-list">${rows}</div>${tools}
    </section>`;
  }

  function waveButtons(context) {
    return draft.waves.map((wave, index) => {
      const total = waveTotal(index);
      return `<button class="attack-wave-btn ${draft.activeWave === index ? 'active' : ''} ${total ? 'has-troops' : ''}" data-action="attack-plan-wave" data-wave="${index}">
        <span>Vaga ${index + 1}</span>
        <small data-wave-total="${index}">${context.format(total)} tropas</small>
      </button>`;
    }).join('');
  }

  function availabilityRows(context) {
    const profile = context.getProfile();
    const troopMeta = context.getTroopsMeta();
    return Object.entries(troopMeta).map(([type, troop]) => {
      const assigned = assignedByType(type);
      const available = Math.max(0, Number(profile.troops[type]) || 0);
      const remaining = Math.max(0, available - assigned);
      return `<div class="attack-availability-row">
        <span>${troop.icon} ${context.escapeHtml(troop.name)}</span>
        <strong data-remaining-type="${type}">${context.format(remaining)}</strong>
        <small>de ${context.format(available)}</small>
      </div>`;
    }).join('');
  }

  function orderStrip(context) {
    return draft.waves.map((wave, index) => `<div class="attack-order-step ${waveTotal(index) ? 'active' : ''}">
      <b>${index + 1}</b><span>Vaga ${index + 1}</span><small data-order-total="${index}">${context.format(waveTotal(index))}</small>
    </div>`).join('<i>→</i>');
  }

  function resourceLine(target, context) {
    if (globalThis.ReinosResourceIcons?.line) return globalThis.ReinosResourceIcons.line(target.loot, context.format);
    return `Madeira ${context.format(target.loot.wood)} · Pedra ${context.format(target.loot.stone)} · Comida ${context.format(target.loot.food)} · Moedas ${context.format(target.loot.coins)}`;
  }

  function plannerHtml(context) {
    const target = getTarget();
    const defense = context.getAttackDefense?.(target) || globalThis.RealmFalkenEspionage?.generateDefense?.(target);
    const activeWave = draft.activeWave;
    const falkenProgress = target.targetType === 'player' ? null : globalThis.ReinosFalkenProgression?.progress(target);
    const targetProgressLabel = falkenProgress ? (falkenProgress.maxed ? 'Nível máximo' : `${falkenProgress.current}/${falkenProgress.needed} vitórias`) : '';
    const targetPortrait = target.portraitType === 'player'
      ? '<span class="attack-player-castle">🏰</span>'
      : target.targetType === 'outpost'
        ? '<span class="attack-player-castle">🏯</span>'
        : '<img src="../assets/barao_falken.png" alt="Barão Falken" />';
    return `<div id="attack-planner" class="attack-planner-shell">
      <header class="attack-target-strip">
        <div class="attack-target-portrait">${targetPortrait}</div>
        <div class="attack-target-main">
          <small>Alvo do ataque</small>
          <strong>${context.escapeHtml(target.name)} • Nível ${target.level}</strong>
          ${targetProgressLabel ? `<span>Progresso: ${context.escapeHtml(targetProgressLabel)}</span>` : ''}
          <span>Coordenadas X ${target.x} • Y ${target.y}</span>
        </div>
        <div class="attack-target-stat"><small>Defensores observados</small><b>${context.format(defense?.totalUnits || 0)}</b></div>
        <div class="attack-target-stat loot"><small>Saque máximo</small><b>${resourceLine(target, context)}</b></div>
      </header>

      <div class="attack-planner-layout">
        <aside class="attack-wave-panel">
          <h4>Ordem do ataque</h4>
          <p>As vagas avançam pela ordem indicada.</p>
          <div class="attack-wave-list">${waveButtons(context)}</div>
          <button class="secondary-btn attack-panel-btn" data-action="attack-plan-clear-wave" data-wave="${activeWave}">Limpar vaga</button>
        </aside>

        <main class="attack-formation-panel">
          <div class="attack-section-heading">
            <div><small>Configurar</small><h4>Vaga ${activeWave + 1}</h4></div>
            <span>Ativa apenas os flancos que pretendes atacar nesta vaga.</span>
          </div>
          <div class="attack-flank-grid">${FLANKS.map(flank => flankCard(flank, activeWave, context)).join('')}</div>
          <div class="attack-order-strip"><span>Sequência:</span>${orderStrip(context)}</div>
        </main>

        <aside class="attack-summary-panel">
          <h4>Tropas disponíveis</h4>
          <div class="attack-availability-list">${availabilityRows(context)}</div>
          ${globalThis.ReinosAttackTools?.summaryHtml?.(context) || ''}
          <div class="attack-summary-box"><span>Total colocado</span><strong data-attack-total>${context.format(totalAssigned())}</strong></div>
          <div class="attack-summary-box"><span>Força de ataque</span><strong data-attack-strength>${context.format(attackStrength())}</strong></div>
          <div class="attack-summary-box"><span>Flancos escolhidos</span><strong data-selected-flanks>${selectedFlanks().size}</strong></div>
          <button class="action-btn attack-panel-btn" data-action="attack-plan-autofill" data-wave="${activeWave}">Distribuir automaticamente</button>
          <button class="secondary-btn attack-panel-btn" data-action="attack-plan-clear-all">Limpar tudo</button>
        </aside>
      </div>
    </div>`;
  }

  function renderPlanner() {
    if (!draft) return;
    const root = document.querySelector('#attack-planner');
    if (!root) return;
    const wrapper = document.createElement('div');
    wrapper.innerHTML = plannerHtml(draft.context);
    root.replaceWith(wrapper.firstElementChild);
  }

  function updateSummaries() {
    if (!draft) return;
    const context = draft.context;
    const profile = context.getProfile();
    const troopMeta = context.getTroopsMeta();
    Object.keys(troopMeta).forEach(type => {
      const remaining = Math.max(0, (Number(profile.troops[type]) || 0) - assignedByType(type));
      const element = document.querySelector(`[data-remaining-type="${type}"]`);
      if (element) element.textContent = context.format(remaining);
    });
    const totalElement = document.querySelector('[data-attack-total]');
    if (totalElement) totalElement.textContent = context.format(totalAssigned());
    const strengthElement = document.querySelector('[data-attack-strength]');
    if (strengthElement) strengthElement.textContent = context.format(attackStrength());
    const selectedElement = document.querySelector('[data-selected-flanks]');
    if (selectedElement) selectedElement.textContent = String(selectedFlanks().size);

    draft.waves.forEach((wave, index) => {
      const total = waveTotal(index);
      document.querySelectorAll(`[data-wave-total="${index}"]`).forEach(element => { element.textContent = `${context.format(total)} tropas`; });
      document.querySelectorAll(`[data-order-total="${index}"]`).forEach(element => { element.textContent = context.format(total); });
      const waveButton = document.querySelector(`.attack-wave-btn[data-wave="${index}"]`);
      waveButton?.classList.toggle('has-troops', total > 0);
      const orderStep = document.querySelectorAll('.attack-order-step')[index];
      orderStep?.classList.toggle('active', total > 0);
    });

    FLANKS.forEach(flank => {
      const total = flankTotal(draft.activeWave, flank.id);
      const element = document.querySelector(`[data-flank-total="${flank.id}"]`);
      if (element) element.textContent = context.format(total);
    });
    globalThis.ReinosAttackTools?.updateSummaries?.(context);
  }

  function openTarget(target, context) {
    if (!target) return;
    if (Number(target.cooldownUntil) > context.now()) {
      context.toast('Este castelo ainda está protegido após o último ataque.', 'error');
      return;
    }

    draft = createDraft(target, context);
    context.openModal(
      `Preparar ataque a ${context.escapeHtml(target.name)}`,
      plannerHtml(context),
      `<button class="secondary-btn" data-action="modal-close">Cancelar</button>
       <button class="attack-order-btn" data-action="attack-plan-send">⚔️ DAR ORDEM DE ATAQUE</button>`
    );
    const plannerModal = document.querySelector('.modal');
    plannerModal?.classList.add('attack-planner-modal');
    plannerModal?.closest('.modal-backdrop')?.classList.add('attack-planner-backdrop');
  }

  function open(targetId, context) {
    const target = context.getAttackTarget?.(targetId, 'falken') || context.getProfile()?.targets?.find(item => item.id === targetId);
    openTarget(target, context);
  }

  function toggleFlank(waveIndex, flankId) {
    const flank = draft?.waves?.[waveIndex]?.flanks?.[flankId];
    if (!flank) return;
    flank.enabled = !flank.enabled;
    if (!flank.enabled) {
      Object.keys(flank.troops).forEach(type => { flank.troops[type] = 0; });
      globalThis.ReinosAttackTools?.clearFlank?.(waveIndex, flankId);
    }
    renderPlanner();
  }

  function clearWave(waveIndex) {
    const wave = draft?.waves?.[waveIndex];
    if (!wave) return;
    FLANKS.forEach(flank => {
      Object.keys(wave.flanks[flank.id].troops).forEach(type => { wave.flanks[flank.id].troops[type] = 0; });
    });
    renderPlanner();
  }

  function clearAll() {
    draft?.waves?.forEach((wave, index) => clearWaveData(index));
    renderPlanner();
  }

  function clearWaveData(waveIndex) {
    const wave = draft?.waves?.[waveIndex];
    if (!wave) return;
    FLANKS.forEach(flank => Object.keys(wave.flanks[flank.id].troops).forEach(type => { wave.flanks[flank.id].troops[type] = 0; }));
    globalThis.ReinosAttackTools?.clearWave?.(waveIndex);
  }

  function autoFill(waveIndex) {
    if (!draft) return;
    const context = draft.context;
    const profile = context.getProfile();
    const troopMeta = context.getTroopsMeta();
    const wave = draft.waves[waveIndex];
    const enabled = FLANKS.filter(flank => wave.flanks[flank.id].enabled);
    if (!enabled.length) {
      context.toast('Ativa pelo menos um flanco nesta vaga.', 'error');
      return;
    }

    clearWaveData(waveIndex);
    const intelligentlyFilled = globalThis.ReinosIntelligentAutofill?.fill?.({
      draft,
      waveIndex,
      flanks: FLANKS,
      assignedByType
    });
    if (!intelligentlyFilled) {
      Object.keys(troopMeta).forEach(type => {
        const available = Math.max(0, (Number(profile.troops[type]) || 0) - assignedByType(type));
        if (!available) return;
        let remaining = available;
        enabled.forEach((flank, index) => {
          const slotsLeft = enabled.length - index;
          const amount = index === enabled.length - 1 ? remaining : Math.floor(remaining / slotsLeft);
          wave.flanks[flank.id].troops[type] = amount;
          remaining -= amount;
        });
      });
    }
    renderPlanner();
  }

  function normalizeNonEmptyPlan() {
    if (!draft) return [];
    return clonePlanWaves(draft.waves).map(wave => {
      FLANKS.forEach(flank => {
        const entry = wave.flanks[flank.id];
        if (!entry.enabled) {
          Object.keys(entry.troops).forEach(type => { entry.troops[type] = 0; });
          entry.tools = globalThis.ReinosSiegeTools?.emptyToolMap?.() || {};
        }
      });
      return wave;
    }).filter(wave => FLANKS.some(flank => Object.values(wave.flanks[flank.id].troops).some(count => count > 0)));
  }

  function currentOrderSnapshot() {
    if (!draft) return null;
    const context = draft.context;
    const profile = context.getProfile();
    const target = getTarget();
    if (!target || Number(target.cooldownUntil) > context.now()) {
      context.toast('O alvo já não está disponível para ataque.', 'error');
      return null;
    }

    const waves = normalizeNonEmptyPlan();
    if (!waves.length) {
      context.toast('Coloca pelo menos uma unidade numa vaga e num flanco.', 'error');
      return null;
    }

    const troopMeta = context.getTroopsMeta();
    const totals = emptyTroops(troopMeta);
    waves.forEach(wave => FLANKS.forEach(flank => Object.entries(wave.flanks[flank.id].troops).forEach(([type, count]) => { totals[type] += count; })));

    for (const [type, count] of Object.entries(totals)) {
      if (count > (Number(profile.troops[type]) || 0)) {
        context.toast(`Já não tens tropas suficientes de ${troopMeta[type].name}.`, 'error');
        return null;
      }
    }

    const toolValidation = globalThis.ReinosAttackTools?.validatePlan?.(waves, context) || { ok: true };
    if (!toolValidation.ok) {
      context.toast(toolValidation.message || 'Ferramentas de cerco inválidas.', 'error');
      return null;
    }

    return { target, waves, totals };
  }

  function reopenPlanner() {
    if (!draft) return;
    const context = draft.context;
    const target = getTarget();
    if (!target) return;
    context.openModal(
      `Preparar ataque a ${context.escapeHtml(target.name)}`,
      plannerHtml(context),
      `<button class="secondary-btn" data-action="modal-close">Cancelar</button>
       <button class="attack-order-btn" data-action="attack-plan-send">⚔️ DAR ORDEM DE ATAQUE</button>`
    );
    const plannerModal = document.querySelector('.modal');
    plannerModal?.classList.add('attack-planner-modal');
    plannerModal?.closest('.modal-backdrop')?.classList.add('attack-planner-backdrop');
  }

  async function commitAttack(selection, preparedOrder) {
    if (!draft || !preparedOrder) return false;
    const context = draft.context;
    const profile = context.getProfile();
    const freshOrder = currentOrderSnapshot();
    if (!freshOrder) {
      reopenPlanner();
      return false;
    }

    const { target, waves, totals } = freshOrder;
    if (target.id !== preparedOrder.target.id) {
      context.toast('O alvo do ataque foi alterado. Confirma novamente a formação.', 'error');
      reopenPlanner();
      return false;
    }

    const cost = selection?.cost || { currency: null, amount: 0 };
    if (cost.currency && (Number(profile.resources?.[cost.currency]) || 0) < (Number(cost.amount) || 0)) {
      context.toast('Não tens recursos suficientes para a montada selecionada.', 'error');
      return false;
    }

    Object.entries(totals).forEach(([type, count]) => { profile.troops[type] -= count; });
    globalThis.ReinosAttackTools?.consumePlan?.(profile, waves);
    if (cost.currency && cost.amount > 0) profile.resources[cost.currency] -= cost.amount;

    const travel = {
      version: globalThis.ReinosTravelPlanning?.VERSION || 1,
      stableLevel: Number(selection?.stableLevel) || 0,
      mountId: selection?.mountId || 'on-foot',
      mountName: selection?.mountName || 'Sem montada',
      reduction: Number(selection?.reduction) || 0,
      baseTravelMs: Number(selection?.baseTravelMs) || Number(selection?.travelMs) || 1000,
      travelMs: Number(selection?.travelMs) || Number(selection?.baseTravelMs) || 1000,
      returnTravelMs: Number(selection?.returnTravelMs) || Number(selection?.travelMs) || 1000,
      cost: { currency: cost.currency || null, amount: Math.max(0, Math.floor(Number(cost.amount) || 0)) }
    };

    const commanderData = globalThis.ReinosEquipmentSystem?.missionCommander?.(profile) || {};
    const mission = context.createMarchMission?.(target, totals, {
      attackPlan: { version: PLAN_VERSION, waves },
      targetType: target.targetType || 'falken',
      targetUserId: target.targetUserId || null,
      travelPlan: travel,
      baseTravelMs: travel.baseTravelMs,
      travelMs: travel.travelMs,
      ...commanderData
    });

    if (mission) {
      profile.missions.push(mission);
    } else {
      const originPosition = { ...(profile.worldPosition || { x: 0, y: 0 }) };
      const targetPosition = { x: Number(target.x) || 0, y: Number(target.y) || 0 };
      const route = globalThis.ReinosMarchTravelTime?.route?.(originPosition, targetPosition);
      const fallbackTravelMs = Math.max(1000, Math.round(Math.hypot(targetPosition.x - originPosition.x, targetPosition.y - originPosition.y) * 6000));
      const travelMs = travel.travelMs || route?.travelMs || fallbackTravelMs;
      const startedAt = context.now();
      profile.missions.push({
        id: context.uid('m'),
        targetId: target.id,
        targetName: target.name,
        targetType: target.targetType || 'falken',
        targetUserId: target.targetUserId || null,
        troops: totals,
        attackPlan: { version: PLAN_VERSION, waves },
        travelPlan: travel,
        state: 'outgoing',
        targetPosition,
        originPosition,
        distanceCoordinates: route?.distance ?? null,
        coordinatesPerMinute: route?.distance && travelMs ? route.distance / (travelMs / 60000) : route?.coordinatesPerMinute ?? null,
        baseTravelMs: travel.baseTravelMs,
        travelMs,
        startedAt,
        launchStartedAt: startedAt,
        cancelWindowEndsAt: startedAt + (globalThis.ReinosAttackCancellation?.CANCEL_WINDOW_MS || 10 * 60 * 1000),
        endAt: startedAt + travelMs,
        ...commanderData
      });
    }

    context.closeModal();
    context.renderGame();
    const mountText = travel.mountId === 'on-foot' ? 'sem montada' : `com ${travel.mountName}`;
    context.toast(`A ordem foi dada. ${waves.length} vaga(s) avançam para ${target.name} ${mountText}.`, 'success');
    await context.persist(true);
    const finishedDraft = draft;
    draft = null;
    globalThis.ReinosAttackTools?.releaseDraft?.(finishedDraft);
    return true;
  }

  async function sendAttack() {
    if (!draft) return;
    const order = currentOrderSnapshot();
    if (!order) return;
    const context = draft.context;

    if (globalThis.ReinosTravelPlanning?.open) {
      globalThis.ReinosTravelPlanning.open(order, context, {
        onBack: async () => reopenPlanner(),
        onConfirm: async selection => commitAttack(selection, order)
      });
      return;
    }

    const route = globalThis.ReinosMarchTravelTime?.route?.(
      context.getProfile()?.worldPosition,
      { x: Number(order.target.x) || 0, y: Number(order.target.y) || 0 }
    );
    await commitAttack({
      stableLevel: 0,
      mountId: 'on-foot',
      mountName: 'Sem montada',
      reduction: 0,
      baseTravelMs: route?.travelMs || 1000,
      travelMs: route?.travelMs || 1000,
      returnTravelMs: route?.travelMs || 1000,
      cost: { currency: null, amount: 0 }
    }, order);
  }

  function troopsPower(troops, troopMeta, mode = 'attack') {
    return Object.entries(troops || {}).reduce((sum, [type, count]) => sum + (Number(count) || 0) * (Number(troopMeta[type]?.[mode]) || 0), 0);
  }

  function totalTroops(troops) {
    return Object.values(troops || {}).reduce((sum, count) => sum + (Number(count) || 0), 0);
  }

  function reduceTroops(troops, lossRate, random) {
    const survivors = {};
    Object.entries(troops || {}).forEach(([type, count]) => {
      const safeCount = Math.max(0, Math.floor(Number(count) || 0));
      const adjusted = Math.max(0, Math.min(1, lossRate * (0.90 + random() * 0.20)));
      const lost = Math.min(safeCount, Math.round(safeCount * adjusted));
      survivors[type] = safeCount - lost;
    });
    return survivors;
  }

  function addTroopMap(target, source) {
    Object.entries(source || {}).forEach(([type, count]) => { target[type] = (target[type] || 0) + (Number(count) || 0); });
  }

  function subtractTroopMap(target, source) {
    Object.entries(source || {}).forEach(([type, count]) => { target[type] = Math.max(0, (target[type] || 0) - (Number(count) || 0)); });
  }

  function seededRandom(seedText) {
    let state = 2166136261;
    for (const char of String(seedText || 'attack')) {
      state ^= char.charCodeAt(0);
      state = Math.imul(state, 16777619);
    }
    return () => {
      state += 0x6D2B79F5;
      let value = state;
      value = Math.imul(value ^ (value >>> 15), value | 1);
      value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
      return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
    };
  }

  function resolveBattle(mission, context) {
    if (!mission?.attackPlan?.waves?.length) return false;
    if (mission.targetType === 'player' && context.resolvePlayerBattle) {
      return context.resolvePlayerBattle(mission, context);
    }
    if (mission.targetType === 'outpost' && globalThis.ReinosOutposts?.resolveCaptureBattle) {
      return globalThis.ReinosOutposts.resolveCaptureBattle(mission, context);
    }
    const profile = context.getProfile();
    const target = profile.targets.find(item => item.id === mission.targetId);
    if (!target) return true;

    const troopMeta = context.getTroopsMeta();
    const defense = globalThis.RealmFalkenEspionage?.generateDefense?.(target);
    if (!defense) return false;

    const targetSnapshot = {
      id: target.id,
      name: target.name,
      level: target.level,
      coordinates: { x: target.x, y: target.y }
    };
    const sentTroopsSnapshot = Object.fromEntries(Object.entries(mission.troops || {}).map(([type, count]) => [type, Math.max(0, Math.floor(Number(count) || 0))]));
    const random = seededRandom(`${mission.id}:${target.id}:${target.level}`);
    const defenseInitial = Object.fromEntries(defense.sectors.map(sector => [sector.id, {
      ...sector,
      units: { ...sector.units }
    }]));
    const sectors = Object.fromEntries(defense.sectors.map(sector => [sector.id, {
      ...sector,
      units: { ...sector.units }
    }]));
    const attackBySector = Object.fromEntries(FLANKS.map(flank => [flank.id, {
      sent: emptyTroops(troopMeta),
      wallSurvivors: emptyTroops(troopMeta),
      toolsUsed: globalThis.ReinosSiegeTools?.emptyToolMap?.() || {},
      toolReductions: { wall: 0, gate: 0, ranged: 0 }
    }]));
    const allSurvivors = emptyTroops(troopMeta);
    const courtyardForces = emptyTroops(troopMeta);
    let courtyardSurvivors = emptyTroops(troopMeta);
    const breached = new Set();
    const usedFlanks = new Set();
    let wavesUsed = 0;

    mission.attackPlan.waves.forEach(wave => {
      let waveUsed = false;
      FLANKS.forEach(flankDef => {
        const group = wave.flanks?.[flankDef.id]?.troops || {};
        if (totalTroops(group) < 1) return;
        waveUsed = true;
        usedFlanks.add(flankDef.id);
        addTroopMap(attackBySector[flankDef.id].sent, group);
        const flankTools = wave.flanks?.[flankDef.id]?.tools || {};
        globalThis.ReinosAttackTools?.addToolMap?.(attackBySector[flankDef.id].toolsUsed, flankTools);
        const sector = sectors[flankDef.id];
        const equipmentBonuses = globalThis.ReinosEquipmentSystem?.commanderBonuses?.(profile, mission) || {};
        const rawEffectiveDefense = globalThis.ReinosAttackTools?.applyToSector?.(sector, flankTools, flankDef.id) || {
          reductions: { wall: 0, gate: 0, ranged: 0 },
          wallBonus: Number(sector.wallBonus) || 0,
          gateBonus: Number(sector.gateBonus) || 0,
          rangedBonus: Number(sector.rangedBonus) || 0
        };
        const effectiveDefense = {
          ...rawEffectiveDefense,
          wallBonus: Math.max(0, (Number(rawEffectiveDefense.wallBonus) || 0) - (Number(equipmentBonuses.wallReduction) || 0)),
          gateBonus: Math.max(0, (Number(rawEffectiveDefense.gateBonus) || 0) - (Number(equipmentBonuses.gateReduction) || 0))
        };
        attackBySector[flankDef.id].toolReductions.wall = Math.max(attackBySector[flankDef.id].toolReductions.wall, effectiveDefense.reductions.wall || 0);
        attackBySector[flankDef.id].toolReductions.gate = Math.max(attackBySector[flankDef.id].toolReductions.gate, effectiveDefense.reductions.gate || 0);
        attackBySector[flankDef.id].toolReductions.ranged = Math.max(attackBySector[flankDef.id].toolReductions.ranged, effectiveDefense.reductions.ranged || 0);
        const wasBreached = breached.has(flankDef.id) || totalTroops(sector.units) < 1;

        if (wasBreached) {
          addTroopMap(allSurvivors, group);
          addTroopMap(courtyardForces, group);
          addTroopMap(attackBySector[flankDef.id].wallSurvivors, group);
          breached.add(flankDef.id);
          return;
        }

        const baseAttackerPower = troopsPower(group, troopMeta, 'attack');
        const attackerPower = (globalThis.ReinosEquipmentSystem?.applyCommanderAttackPower?.(profile, mission, baseAttackerPower) || baseAttackerPower) * (0.94 + random() * 0.12);
        const fortification = 1 + ((Number(effectiveDefense.wallBonus) || 0) + (Number(effectiveDefense.gateBonus) || 0) * 0.7 + (Number(effectiveDefense.rangedBonus) || 0) * 0.45) / 100;
        const defenderPower = Math.max(1, troopsPower(sector.units, troopMeta, 'defense') * fortification * (0.95 + random() * 0.10));
        const ratio = attackerPower / defenderPower;
        const attackerLossRate = ratio >= 1
          ? context.clamp(0.12 + 0.26 / Math.max(ratio, 0.7), 0.08, 0.42)
          : context.clamp(0.60 + (1 - ratio) * 0.23, 0.52, 0.94);
        const defenderLossRate = ratio >= 1
          ? context.clamp(0.82 + (ratio - 1) * 0.14, 0.82, 1)
          : context.clamp(0.12 + ratio * 0.46, 0.12, 0.66);

        const groupSurvivors = reduceTroops(group, attackerLossRate, random);
        const defenderSurvivors = reduceTroops(sector.units, defenderLossRate, random);
        sector.units = defenderSurvivors;
        addTroopMap(allSurvivors, groupSurvivors);
        addTroopMap(attackBySector[flankDef.id].wallSurvivors, groupSurvivors);

        if (totalTroops(defenderSurvivors) === 0) {
          breached.add(flankDef.id);
          addTroopMap(courtyardForces, groupSurvivors);
        }
      });
      if (waveUsed) wavesUsed += 1;
    });

    const defendersBeforeCourtyard = Object.values(sectors).reduce((sum, sector) => sum + totalTroops(sector.units), 0);
    const remainingDefensePower = Object.values(sectors).reduce((sum, sector) => sum + troopsPower(sector.units, troopMeta, 'defense'), 0);
    const breachCount = breached.size;
    const courtyardBonus = breachCount >= 3 ? 1.30 : breachCount === 2 ? 1.10 : breachCount === 1 ? 0.76 : 0;
    const baseCourtyardAttack = troopsPower(courtyardForces, troopMeta, 'attack');
    const courtyardAttack = (globalThis.ReinosEquipmentSystem?.applyCommanderAttackPower?.(profile, mission, baseCourtyardAttack) || baseCourtyardAttack) * courtyardBonus * (0.95 + random() * 0.10);
    const courtyardDefense = target.strength * 0.78 + remainingDefensePower * 0.22;
    const win = breachCount > 0 && courtyardAttack >= courtyardDefense;

    if (totalTroops(courtyardForces) > 0) {
      const ratio = courtyardAttack / Math.max(courtyardDefense, 1);
      const courtyardLossRate = win
        ? context.clamp(0.12 + 0.24 / Math.max(ratio, 0.8), 0.08, 0.38)
        : context.clamp(0.62 + (1 - ratio) * 0.18, 0.54, 0.92);
      courtyardSurvivors = reduceTroops(courtyardForces, courtyardLossRate, random);
      subtractTroopMap(allSurvivors, courtyardForces);
      addTroopMap(allSurvivors, courtyardSurvivors);
    }

    if (!context.finishBattleMission) {
      Object.entries(allSurvivors).forEach(([type, count]) => { profile.troops[type] += count; });
    }
    const sentTotal = totalTroops(sentTroopsSnapshot);
    const survivorTotal = totalTroops(allSurvivors);
    const lostTotal = Math.max(0, sentTotal - survivorTotal);
    const carry = Object.entries(allSurvivors).reduce((sum, [type, count]) => sum + count * (Number(troopMeta[type]?.carry) || 0), 0);
    profile.totalBattles += 1;

    const flankNames = [...usedFlanks].map(id => FLANKS.find(flank => flank.id === id)?.name || id).join(', ');
    const breachedNames = [...breached].map(id => FLANKS.find(flank => flank.id === id)?.name || id).join(', ') || 'nenhum';
    const commonReportData = {
      targetId: targetSnapshot.id,
      attackerName: context.getCurrentUser?.()?.username,
      attackerKingdom: profile.kingdomName,
      attackerLevel: profile.level,
      defenderName: targetSnapshot.name,
      defenderLevel: targetSnapshot.level,
      coordinates: targetSnapshot.coordinates,
      wavesUsed,
      usedFlanks: [...usedFlanks],
      breachedFlanks: [...breached],
      courtyardBonus,
      troopsSent: sentTroopsSnapshot,
      troopsSurvived: allSurvivors,
      toolsUsed: globalThis.ReinosAttackTools?.summarizePlan?.(mission.attackPlan?.waves) || {},
      attackBySector,
      defenseInitial,
      defenseFinal: sectors,
      courtyard: {
        attackersEntered: courtyardForces,
        attackersSurvived: courtyardSurvivors,
        defendersBefore: defendersBeforeCourtyard,
        defendersAfter: win ? 0 : defendersBeforeCourtyard,
        attackPower: courtyardAttack,
        defensePower: courtyardDefense
      }
    };

    if (win) {
      profile.totalVictories += 1;
      const lootFactor = carry > 0 ? context.clamp(carry / 1200, 0.18, 1) : 0;
      const lootBonus = globalThis.ReinosEquipmentSystem?.lootMultiplier?.(profile, mission) || 1;
      const loot = Object.fromEntries(Object.entries(target.loot).map(([key, value]) => [key, Math.round(value * lootFactor * lootBonus)]));
      if (!context.finishBattleMission) context.addResources(loot);
      const defeatedLevel = target.level;
      const honor = 20 + defeatedLevel * 12;
      const xpGained = 90 + defeatedLevel * 45;
      profile.honor += honor;
      context.gainXp(xpGained);
      const progressionResult = globalThis.ReinosFalkenProgression?.registerVictory(target, {
        strengthFn: context.falkenStrength,
        lootFn: context.falkenLoot
      }) || { level: target.level, current: 0, needed: 1, leveledUp: false, maxed: false };
      const progressionMessage = globalThis.ReinosFalkenProgression?.victoryMessage(target, progressionResult) || 'Progresso do Barão atualizado.';
      const equipmentDrop = globalThis.ReinosEquipmentSystem?.awardBaronDrop?.(context, target, { defeatedLevel, win: true, mission, suppressReport: true }) || null;
      target.cooldownUntil = context.now() + 45000;
      if (context.finishBattleMission) context.finishBattleMission(mission, allSurvivors, loot, true);
      if (globalThis.RealmBattleReports?.create) {
        globalThis.RealmBattleReports.create(context, {
          ...commonReportData,
          victory: true,
          honorDelta: honor,
          xpGained,
          loot,
          equipmentDrop
        });
      } else {
        profile.reports.push({
          id: context.uid('r'),
          at: context.now(),
          title: `Batalha em ${target.name}`,
          result: 'win',
          text: `Vitória com ${wavesUsed} vaga(s). Flancos atacados: ${flankNames}. Brechas abertas: ${breachedNames}. Perdeste ${lostTotal} unidades e saqueaste madeira ${context.format(loot.wood)}, pedra ${context.format(loot.stone)}, comida ${context.format(loot.food)} e moedas ${context.format(loot.coins)}. +${honor} honra. ${progressionMessage}`
        });
      }
      context.toast(`Vitória! O plano abriu ${breachCount} brecha(s). ${progressionMessage}`, 'success');
    } else {
      const honorBefore = profile.honor;
      profile.honor = Math.max(0, profile.honor - 8);
      const honorDelta = profile.honor - honorBefore;
      const xpGained = 25 + target.level * 10;
      context.gainXp(xpGained);
      target.cooldownUntil = context.now() + 25000;
      const emptyLoot = { wood: 0, stone: 0, food: 0, coins: 0 };
      if (context.finishBattleMission) context.finishBattleMission(mission, allSurvivors, emptyLoot, false);
      if (globalThis.RealmBattleReports?.create) {
        globalThis.RealmBattleReports.create(context, {
          ...commonReportData,
          victory: false,
          honorDelta,
          xpGained,
          loot: emptyLoot
        });
      } else {
        profile.reports.push({
          id: context.uid('r'),
          at: context.now(),
          title: `Batalha em ${target.name}`,
          result: 'loss',
          text: `Derrota com ${wavesUsed} vaga(s). Flancos atacados: ${flankNames}. Brechas abertas: ${breachedNames}. Perdeste ${lostTotal} unidades. As tropas sobreviventes regressaram ao castelo.`
        });
      }
      context.toast(`O ataque foi derrotado. Foram abertas ${breachCount} brecha(s).`, 'error');
    }
    return true;
  }

  async function handleAction(action, actionElement, context) {
    if (action === 'attack-plan-wave') {
      if (!draft) return true;
      draft.activeWave = Math.max(0, Math.min(WAVE_COUNT - 1, Number(actionElement.dataset.wave) || 0));
      renderPlanner();
      return true;
    }
    if (action === 'attack-plan-toggle-flank') {
      toggleFlank(Number(actionElement.dataset.wave), actionElement.dataset.flank);
      return true;
    }
    if (action === 'attack-plan-clear-wave') {
      clearWave(Number(actionElement.dataset.wave));
      return true;
    }
    if (action === 'attack-plan-clear-all') {
      clearAll();
      return true;
    }
    if (action === 'attack-plan-autofill') {
      autoFill(Number(actionElement.dataset.wave));
      return true;
    }
    if (action === 'attack-plan-send') {
      await sendAttack();
      return true;
    }
    return false;
  }

  document.addEventListener('input', event => {
    const input = event.target.closest?.('.attack-plan-input');
    if (!input || !draft) return;
    const waveIndex = Number(input.dataset.wave);
    const flankId = input.dataset.flank;
    const type = input.dataset.type;
    const profile = draft.context.getProfile();
    const flank = draft.waves?.[waveIndex]?.flanks?.[flankId];
    if (!flank?.enabled || !Object.hasOwn(flank.troops, type)) return;

    const otherAssigned = assignedByType(type, { waveIndex, flankId });
    const maximum = Math.max(0, (Number(profile.troops[type]) || 0) - otherAssigned);
    const value = draft.context.clamp(Math.floor(Number(input.value) || 0), 0, maximum);
    flank.troops[type] = value;
    if (String(value) !== input.value) input.value = String(value);
    updateSummaries();
  });

  globalThis.RealmAttackPlanner = Object.freeze({
    PLAN_VERSION,
    open,
    openTarget,
    handleAction,
    resolveBattle
  });
})();
