'use strict';

/**
 * Interações entre jogadores no mapa mundial.
 *
 * Responsabilidades:
 * - abrir as opções Espiar e Atacar ao clicar noutro castelo;
 * - criar relatórios de espionagem de jogadores;
 * - fornecer alvos e defesas ao planeador de ataque;
 * - resolver batalhas jogador contra jogador sem alterar os Barões Falken.
 */
(() => {
  const SPY_REPORT_TYPE = 'player-spy';
  const TARGET_PREFIX = 'player:';
  const FLANKS = Object.freeze([
    { id: 'left', name: 'Flanco esquerdo' },
    { id: 'center', name: 'Centro' },
    { id: 'right', name: 'Flanco direito' }
  ]);

  function safeNumber(value) {
    return Math.max(0, Math.floor(Number(value) || 0));
  }

  function cloneTroops(troops, troopMeta = {}) {
    const keys = new Set([...Object.keys(troopMeta || {}), ...Object.keys(troops || {})]);
    return Object.fromEntries([...keys].map(type => [type, safeNumber(troops?.[type])]));
  }

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

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

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

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

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

  function keepLevel(profile) {
    return Math.max(1, safeNumber(profile?.buildings?.find(building => building.type === 'keep')?.level) || 1);
  }

  function watchtowerDefense(profile) {
    return (profile?.buildings || [])
      .filter(building => building.type === 'watchtower' && building.status !== 'constructing')
      .reduce((sum, building) => sum + Math.max(1, safeNumber(building.level)) * 180, 0);
  }

  function userIdFromTargetId(targetId) {
    const value = String(targetId || '');
    return value.startsWith(TARGET_PREFIX) ? value.slice(TARGET_PREFIX.length) : value;
  }

  function findUser(context, userId) {
    return context.getDb?.()?.users?.find(user => user.id === userId) || null;
  }

  function findProfile(context, userId) {
    return context.getDb?.()?.profiles?.[userId] || null;
  }

  function resourceLoot(profile) {
    const resources = profile?.resources || {};
    return {
      wood: Math.min(20000, Math.floor(safeNumber(resources.wood) * 0.18)),
      stone: Math.min(20000, Math.floor(safeNumber(resources.stone) * 0.18)),
      food: Math.min(20000, Math.floor(safeNumber(resources.food) * 0.18)),
      coins: Math.min(12000, Math.floor(safeNumber(resources.coins) * 0.12))
    };
  }

  function makeTarget(context, userIdOrTargetId) {
    const userId = userIdFromTargetId(userIdOrTargetId);
    const currentUser = context.getCurrentUser?.();
    if (!userId || userId === currentUser?.id) return null;
    const user = findUser(context, userId);
    const targetProfile = findProfile(context, userId);
    if (!user || !targetProfile?.worldPosition) return null;
    const troopMeta = context.getTroopsMeta?.() || {};
    const strength = Math.round(troopsPower(targetProfile.troops, troopMeta, 'defense') + watchtowerDefense(targetProfile) + keepLevel(targetProfile) * 120);
    return {
      id: `${TARGET_PREFIX}${user.id}`,
      targetType: 'player',
      targetUserId: user.id,
      name: targetProfile.kingdomName || `Reino de ${user.username}`,
      rulerName: user.username,
      level: Math.max(1, safeNumber(targetProfile.level) || 1),
      x: Math.round(Number(targetProfile.worldPosition.x) || 0),
      y: Math.round(Number(targetProfile.worldPosition.y) || 0),
      strength: Math.max(1, strength),
      loot: resourceLoot(targetProfile),
      cooldownUntil: safeNumber(targetProfile.pvpProtectionUntil),
      portraitType: 'player'
    };
  }

  function splitCount(count) {
    const safe = safeNumber(count);
    const left = Math.floor(safe * 0.25);
    const right = Math.floor(safe * 0.25);
    return { left, center: safe - left - right, right };
  }

  function generateDefense(target, context) {
    if (!target || target.targetType !== 'player') return null;
    const targetProfile = findProfile(context, target.targetUserId);
    if (!targetProfile) return null;
    const troopMeta = context.getTroopsMeta?.() || {};
    const sectorTroops = Object.fromEntries(FLANKS.map(flank => [flank.id, emptyTroops(troopMeta)]));
    Object.entries(cloneTroops(targetProfile.troops, troopMeta)).forEach(([type, count]) => {
      const split = splitCount(count);
      FLANKS.forEach(flank => { sectorTroops[flank.id][type] = split[flank.id]; });
    });

    const keep = keepLevel(targetProfile);
    const towerBonus = Math.min(45, Math.round(watchtowerDefense(targetProfile) / 90));
    const castleDefense = globalThis.ReinosCastleSystem?.defenseBonuses?.(targetProfile) || { wall: 0, gate: 0, capacity: 0 };
    const sectors = FLANKS.map(flank => ({
      id: flank.id,
      name: flank.name,
      wallBonus: 12 + keep * 5 + towerBonus + castleDefense.wall,
      gateBonus: flank.id === 'center' ? 10 + keep * 6 + castleDefense.gate : 0,
      rangedBonus: 8 + keep * 3 + Math.round(castleDefense.capacity / 3),
      units: sectorTroops[flank.id]
    }));

    return {
      targetType: 'player',
      targetUserId: target.targetUserId,
      targetName: target.name,
      targetLevel: target.level,
      coordinates: { x: target.x, y: target.y },
      accuracy: 100,
      counterEspionage: Math.min(90, keep * 3),
      totalUnits: totalTroops(targetProfile.troops),
      wallLevel: targetProfile.castleState?.wallLevel || keep,
      gateLevel: targetProfile.castleState?.gateLevel || Math.max(1, Math.ceil(keep / 2)),
      moatLevel: 0,
      tools: { stones: towerBonus, fireArrows: Math.floor(towerBonus / 2), reinforcements: 0 },
      loot: resourceLoot(targetProfile),
      sectors
    };
  }

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

  function openActions(context, userId) {
    const target = makeTarget(context, userId);
    if (!target) return;
    const protectedNow = target.cooldownUntil > context.now();
    context.openModal(
      `${context.escapeHtml(target.name)} • Nível ${target.level}`,
      `<div class="player-action-card">
        <div class="player-action-castle">🏰</div>
        <div class="player-action-info">
          <strong>${context.escapeHtml(target.name)}</strong>
          <span>Governante: ${context.escapeHtml(target.rulerName)}</span>
          <span>Coordenadas X ${target.x} • Y ${target.y}</span>
          <span>Poder defensivo estimado: ${context.format(target.strength)}</span>
          <span>Saque máximo: ${resourceLine(target.loot, context)}</span>
        </div>
      </div>
      <p class="player-action-help">Espia este jogador para conhecer as tropas e a formação defensiva ou prepara um ataque.</p>
      <div class="player-action-options">
        <button class="player-choice spy" data-action="player-spy" data-user-id="${context.escapeHtml(target.targetUserId)}">
          <b>🔍 Espiar</b><small>Receber relatório militar</small>
        </button>
        <button class="player-choice attack" data-action="player-attack-open" data-user-id="${context.escapeHtml(target.targetUserId)}" ${protectedNow ? 'disabled' : ''}>
          <b>⚔️ ${protectedNow ? `Protegido por ${context.formatTime(target.cooldownUntil - context.now())}` : 'Atacar'}</b>
          <small>${protectedNow ? 'Podes continuar a espiar' : 'Escolher flancos e tropas'}</small>
        </button>
        <button class="player-choice market" data-action="market-open-target" data-user-id="${context.escapeHtml(target.targetUserId)}" ${context.hasBuilding?.('marketplace') ? '' : 'disabled'}>
          <b>⚖️ Enviar recursos</b><small>${context.hasBuilding?.('marketplace') ? 'Usar carroças do Mercado' : 'Precisas de Mercado'}</small>
        </button>
      </div>`,
      '<button class="secondary-btn" data-action="modal-close">Fechar</button>'
    );
    document.querySelector('.modal')?.classList.add('player-action-modal');
  }

  async function spy(context, userId) {
    const target = makeTarget(context, userId);
    if (!target) return;
    const defense = generateDefense(target, context);
    if (!defense) return;
    const profile = context.getProfile();
    const report = {
      id: context.uid('pspy'),
      at: context.now(),
      title: `Espionagem: ${target.name} • Nv. ${target.level}`,
      result: 'spy',
      reportType: SPY_REPORT_TYPE,
      targetId: target.id,
      targetUserId: target.targetUserId,
      targetName: target.name,
      targetLevel: target.level,
      targetCoordinates: { x: target.x, y: target.y },
      text: `A espionagem revelou ${defense.totalUnits} defensores do jogador ${target.rulerName}.`,
      spyData: defense
    };
    profile.reports.push(report);
    await context.persist(true);
    openSpyReport(context, report.id);
    context.toast('Relatório de espionagem do jogador recebido.', 'success');
  }

  function findReport(context, reportId) {
    return context.getProfile?.()?.reports?.find(report => report.id === reportId) || null;
  }

  function openSpyReport(context, reportId) {
    const report = findReport(context, reportId);
    if (!report?.spyData) return;
    const target = makeTarget(context, report.targetUserId);
    const data = report.spyData;
    const protectedNow = target && target.cooldownUntil > context.now();
    context.openModal(
      'Resultados de espionagem',
      `<div class="spy-report-sheet player-spy-sheet">
        <div class="spy-report-ribbon">Resultados de espionagem</div>
        <p class="spy-intro">A espionagem foi concluída com sucesso. Os agentes observaram as tropas, fortificações e recursos do castelo deste jogador.</p>
        <div class="spy-target-title"><span>Espiar:</span><strong>${context.escapeHtml(report.targetName)} • Nv. ${report.targetLevel}</strong></div>
        <div class="spy-versus">
          <div class="spy-lord-card player"><div class="spy-avatar">♛</div><b>${context.escapeHtml(context.getCurrentUser()?.username || '')}</b><span>Atacante</span></div>
          <div class="spy-accuracy"><div>🔍 <b>${data.accuracy}%</b></div><div>🎭 <b>${data.counterEspionage}%</b></div><small>Precisão / contraespionagem</small></div>
          <div class="spy-lord-card enemy"><div class="spy-avatar">🏰</div><b>${context.escapeHtml(report.targetName)}</b><span>Nível ${report.targetLevel}</span><small>X ${data.coordinates.x} • Y ${data.coordinates.y}</small></div>
        </div>
        <div class="spy-summary-icons">
          <div><span>⚔️</span><b>${context.format(data.totalUnits)}</b><small>Defensores</small></div>
          <div><span>🧱</span><b>${context.format(data.wallLevel)}</b><small>Muralha</small></div>
          <div><span>🏹</span><b>${context.format(data.tools.fireArrows)}</b><small>Defesas</small></div>
        </div>
        <div class="spy-loot"><b>Saque observado</b><span>${resourceLine(data.loot, context)}</span></div>
      </div>`,
      `<button class="secondary-btn" data-action="modal-close">Fechar</button>
       <button class="secondary-btn" data-action="player-spy-details" data-report-id="${context.escapeHtml(report.id)}">📋 Detalhes</button>
       <button class="action-btn" data-action="player-attack-open" data-user-id="${context.escapeHtml(report.targetUserId)}" ${protectedNow ? 'disabled' : ''}>⚔️ ${protectedNow ? 'Castelo protegido' : 'Atacar agora'}</button>`
    );
    document.querySelector('.modal')?.classList.add('spy-modal');
  }

  function troopRows(units, context) {
    const meta = context.getTroopsMeta?.() || {};
    return Object.entries(meta).map(([type, troop]) => `<div class="defense-unit"><span>${troop.icon} ${context.escapeHtml(troop.name)}</span><b>${context.format(units?.[type] || 0)}</b></div>`).join('');
  }

  function openSpyDetails(context, reportId) {
    const report = findReport(context, reportId);
    if (!report?.spyData) return;
    const data = report.spyData;
    context.openModal(
      `Formação defensiva — ${context.escapeHtml(report.targetName)}`,
      `<div class="defense-report-head">
        <div><small>Relatório recolhido em</small><b>${new Date(report.at).toLocaleString('pt-PT')}</b></div>
        <div><small>Nível observado</small><b>${report.targetLevel}</b></div>
        <div><small>Total de defensores</small><b>${context.format(data.totalUnits)}</b></div>
      </div>
      <div class="defense-fortifications"><span>🧱 Muralha Nv. ${data.wallLevel}</span><span>🚪 Portão Nv. ${data.gateLevel}</span><span>🎭 Contraespionagem ${data.counterEspionage}%</span></div>
      <div class="defense-formation-grid">${data.sectors.map(sector => `<article class="defense-sector ${sector.id}"><h4>${context.escapeHtml(sector.name)}</h4><div class="defense-bonuses"><span>🧱 Muralha +${sector.wallBonus}%</span>${sector.gateBonus ? `<span>🚪 Portão +${sector.gateBonus}%</span>` : ''}<span>🏹 Distância +${sector.rangedBonus}%</span></div><div class="defense-units">${troopRows(sector.units, context)}</div></article>`).join('')}</div>`,
      `<button class="secondary-btn" data-action="player-spy-report-open" data-report-id="${context.escapeHtml(report.id)}">← Voltar ao relatório</button>
       <button class="action-btn" data-action="player-attack-open" data-user-id="${context.escapeHtml(report.targetUserId)}">⚔️ Preparar ataque</button>`
    );
    document.querySelector('.modal')?.classList.add('defense-modal');
  }

  function seededRandom(seedText) {
    let state = 2166136261;
    for (const char of String(seedText || 'pvp')) {
      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 reduceTroops(troops, lossRate, random) {
    const survivors = {};
    Object.entries(troops || {}).forEach(([type, count]) => {
      const safe = safeNumber(count);
      const adjusted = Math.max(0, Math.min(1, lossRate * (0.90 + random() * 0.20)));
      survivors[type] = safe - Math.min(safe, Math.round(safe * adjusted));
    });
    return survivors;
  }

  function resolveBattle(mission, context) {
    if (mission?.targetType !== 'player') return false;
    const target = makeTarget(context, mission.targetUserId || mission.targetId);
    const attackerProfile = context.getProfile();
    const troopMeta = context.getTroopsMeta?.() || {};
    if (!target) {
      context.finishBattleMission?.(mission, cloneTroops(mission.troops, troopMeta), { wood: 0, stone: 0, food: 0, coins: 0 }, false);
      context.toast('O jogador alvo já não está disponível. As tropas estão a regressar.', 'error');
      return true;
    }
    const defenderProfile = findProfile(context, target.targetUserId);
    const defenderUser = findUser(context, target.targetUserId);
    const defense = generateDefense(target, context);
    if (!defenderProfile || !defense) return false;

    const random = seededRandom(`${mission.id}:${target.targetUserId}:${target.level}`);
    const sectors = Object.fromEntries(defense.sectors.map(sector => [sector.id, { ...sector, units: cloneTroops(sector.units, troopMeta) }]));
    const defenseInitial = Object.fromEntries(defense.sectors.map(sector => [sector.id, { ...sector, units: cloneTroops(sector.units, troopMeta) }]));
    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(flank => {
        const group = cloneTroops(wave.flanks?.[flank.id]?.troops, troopMeta);
        if (totalTroops(group) < 1) return;
        waveUsed = true;
        usedFlanks.add(flank.id);
        addTroops(attackBySector[flank.id].sent, group);
        const flankTools = wave.flanks?.[flank.id]?.tools || {};
        globalThis.ReinosAttackTools?.addToolMap?.(attackBySector[flank.id].toolsUsed, flankTools);
        const sector = sectors[flank.id];
        const effectiveDefense = globalThis.ReinosAttackTools?.applyToSector?.(sector, flankTools, flank.id) || {
          reductions: { wall: 0, gate: 0, ranged: 0 },
          wallBonus: Number(sector.wallBonus) || 0,
          gateBonus: Number(sector.gateBonus) || 0,
          rangedBonus: Number(sector.rangedBonus) || 0
        };
        attackBySector[flank.id].toolReductions.wall = Math.max(attackBySector[flank.id].toolReductions.wall, effectiveDefense.reductions.wall || 0);
        attackBySector[flank.id].toolReductions.gate = Math.max(attackBySector[flank.id].toolReductions.gate, effectiveDefense.reductions.gate || 0);
        attackBySector[flank.id].toolReductions.ranged = Math.max(attackBySector[flank.id].toolReductions.ranged, effectiveDefense.reductions.ranged || 0);
        if (breached.has(flank.id) || totalTroops(sector.units) < 1) {
          addTroops(allSurvivors, group);
          addTroops(courtyardForces, group);
          addTroops(attackBySector[flank.id].wallSurvivors, group);
          breached.add(flank.id);
          return;
        }

        const attackPower = troopsPower(group, troopMeta, 'attack') * (0.94 + random() * 0.12);
        const fortification = 1 + (effectiveDefense.wallBonus + effectiveDefense.gateBonus * 0.7 + effectiveDefense.rangedBonus * 0.45) / 100;
        const defensePower = Math.max(1, troopsPower(sector.units, troopMeta, 'defense') * fortification * (0.95 + random() * 0.10));
        const ratio = attackPower / defensePower;
        const attackerLoss = ratio >= 1 ? Math.max(0.08, Math.min(0.42, 0.12 + 0.26 / Math.max(ratio, 0.7))) : Math.max(0.52, Math.min(0.94, 0.60 + (1 - ratio) * 0.23));
        const defenderLoss = ratio >= 1 ? Math.max(0.82, Math.min(1, 0.82 + (ratio - 1) * 0.14)) : Math.max(0.12, Math.min(0.66, 0.12 + ratio * 0.46));
        const groupSurvivors = reduceTroops(group, attackerLoss, random);
        sector.units = reduceTroops(sector.units, defenderLoss, random);
        addTroops(allSurvivors, groupSurvivors);
        addTroops(attackBySector[flank.id].wallSurvivors, groupSurvivors);
        if (totalTroops(sector.units) === 0) {
          breached.add(flank.id);
          addTroops(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 courtyardAttack = troopsPower(courtyardForces, troopMeta, 'attack') * courtyardBonus * (0.95 + random() * 0.10);
    const courtyardDefense = remainingDefensePower + watchtowerDefense(defenderProfile) + keepLevel(defenderProfile) * 120;
    const victory = breachCount > 0 && courtyardAttack >= courtyardDefense;

    if (totalTroops(courtyardForces) > 0) {
      const ratio = courtyardAttack / Math.max(courtyardDefense, 1);
      const loss = victory ? Math.max(0.08, Math.min(0.38, 0.12 + 0.24 / Math.max(ratio, 0.8))) : Math.max(0.54, Math.min(0.92, 0.62 + (1 - ratio) * 0.18));
      courtyardSurvivors = reduceTroops(courtyardForces, loss, random);
      subtractTroops(allSurvivors, courtyardForces);
      addTroops(allSurvivors, courtyardSurvivors);
    }

    const defenseFinal = Object.fromEntries(FLANKS.map(flank => {
      const sector = sectors[flank.id];
      return [flank.id, { ...sector, units: victory ? emptyTroops(troopMeta) : cloneTroops(sector.units, troopMeta) }];
    }));
    const defenderSurvivors = emptyTroops(troopMeta);
    if (!victory) Object.values(defenseFinal).forEach(sector => addTroops(defenderSurvivors, sector.units));
    defenderProfile.troops = defenderSurvivors;
    defenderProfile.pvpProtectionUntil = context.now() + 60000;

    const carry = Object.entries(allSurvivors).reduce((sum, [type, count]) => sum + safeNumber(count) * (Number(troopMeta[type]?.carry) || 0), 0);
    const observedLoot = resourceLoot(defenderProfile);
    const lootFactor = victory && carry > 0 ? Math.max(0.15, Math.min(1, carry / 1500)) : 0;
    const loot = Object.fromEntries(Object.entries(observedLoot).map(([key, value]) => [key, Math.min(safeNumber(defenderProfile.resources?.[key]), Math.round(value * lootFactor))]));
    if (victory) Object.entries(loot).forEach(([key, value]) => { defenderProfile.resources[key] = Math.max(0, safeNumber(defenderProfile.resources[key]) - value); });

    attackerProfile.totalBattles = safeNumber(attackerProfile.totalBattles) + 1;
    if (victory) attackerProfile.totalVictories = safeNumber(attackerProfile.totalVictories) + 1;
    const honorBefore = safeNumber(attackerProfile.honor);
    const honorDelta = victory ? 30 + target.level * 8 : -Math.min(12, honorBefore);
    attackerProfile.honor = Math.max(0, honorBefore + honorDelta);
    const xpGained = victory ? 100 + target.level * 35 : 35 + target.level * 8;
    context.gainXp?.(xpGained);
    context.finishBattleMission?.(mission, allSurvivors, victory ? loot : { wood: 0, stone: 0, food: 0, coins: 0 }, victory);

    const common = {
      targetId: target.id,
      attackerName: context.getCurrentUser?.()?.username,
      attackerKingdom: attackerProfile.kingdomName,
      attackerLevel: attackerProfile.level,
      defenderName: target.name,
      defenderLevel: target.level,
      coordinates: { x: target.x, y: target.y },
      wavesUsed,
      usedFlanks: [...usedFlanks],
      breachedFlanks: [...breached],
      courtyardBonus,
      honorDelta,
      xpGained,
      loot: victory ? loot : { wood: 0, stone: 0, food: 0, coins: 0 },
      troopsSent: cloneTroops(mission.troops, troopMeta),
      troopsSurvived: allSurvivors,
      toolsUsed: globalThis.ReinosAttackTools?.summarizePlan?.(mission.attackPlan?.waves) || {},
      attackBySector,
      defenseInitial,
      defenseFinal,
      courtyard: {
        attackersEntered: courtyardForces,
        attackersSurvived: courtyardSurvivors,
        defendersBefore: defendersBeforeCourtyard,
        defendersAfter: victory ? 0 : totalTroops(defenderSurvivors),
        attackPower: courtyardAttack,
        defensePower: courtyardDefense
      },
      victory
    };
    globalThis.RealmBattleReports?.create?.(context, common);

    const defenderPayload = {
      ...common,
      originCoordinates: { ...(mission.originPosition || attackerProfile.worldPosition || { x: 0, y: 0 }) }
    };
    if (globalThis.ReinosDefenderBattleReports?.create) {
      globalThis.ReinosDefenderBattleReports.create(context, defenderProfile, defenderPayload);
    } else {
      defenderProfile.reports = Array.isArray(defenderProfile.reports) ? defenderProfile.reports : [];
      defenderProfile.reports.push({
        id: context.uid('defense'),
        at: context.now(),
        title: `${victory ? 'Castelo saqueado' : 'Defesa vitoriosa'}: ${attackerProfile.kingdomName}`,
        result: victory ? 'loss' : 'win',
        text: `${context.getCurrentUser?.()?.username || 'Um jogador'} atacou o teu castelo com ${totalTroops(mission.troops)} tropas. ${victory ? `Foram saqueados madeira ${loot.wood}, pedra ${loot.stone}, comida ${loot.food} e moedas ${loot.coins}.` : 'As tuas defesas repeliram o ataque.'}`
      });
    }

    context.toast(victory ? `Vitória contra ${target.name}!` : `O ataque a ${target.name} foi derrotado.`, victory ? 'success' : 'error');
    return true;
  }

  function reportPresentation(report, context) {
    if (report?.reportType !== SPY_REPORT_TYPE || !report.spyData) return null;
    return {
      label: 'Espionagem de jogador',
      className: 'report-spy',
      detailsHtml: `<div>${context.escapeHtml(report.text)}</div><button class="report-open-btn" data-action="player-spy-report-open" data-report-id="${context.escapeHtml(report.id)}">Abrir relatório</button>`
    };
  }

  async function handleAction(action, element, context) {
    if (action === 'player-open') {
      openActions(context, element.dataset.userId);
      return true;
    }
    if (action === 'player-spy') {
      await spy(context, element.dataset.userId);
      return true;
    }
    if (action === 'player-spy-report-open') {
      openSpyReport(context, element.dataset.reportId);
      return true;
    }
    if (action === 'player-spy-details') {
      openSpyDetails(context, element.dataset.reportId);
      return true;
    }
    if (action === 'market-open-target') {
      if (!context.hasBuilding?.('marketplace')) {
        context.toast('Precisas de construir um Mercado para enviar recursos.', 'error');
        return true;
      }
      context.openMarketSend?.(element.dataset.userId);
      return true;
    }
    if (action === 'player-attack-open') {
      const target = makeTarget(context, element.dataset.userId);
      if (!target) return true;
      if (target.cooldownUntil > context.now()) {
        context.toast(`Este castelo está protegido por ${context.formatTime(target.cooldownUntil - context.now())}.`, 'error');
        return true;
      }
      globalThis.RealmAttackPlanner?.openTarget?.(target, context);
      return true;
    }
    return false;
  }

  globalThis.ReinosPlayerInteractions = Object.freeze({
    SPY_REPORT_TYPE,
    TARGET_PREFIX,
    makeTarget,
    generateDefense,
    resolveBattle,
    reportPresentation,
    handleAction
  });
})();
