'use strict';

/**
 * Sistema modular das marchas no mapa mundial.
 *
 * Responsabilidades deste ficheiro:
 * - criar missões com ida e regresso;
 * - desenhar o exército a deslocar-se no mapa mundial;
 * - resolver a batalha ao chegar ao alvo;
 * - devolver sobreviventes e saque apenas quando regressam ao castelo.
 */
(() => {
  const MARCH_VERSION = 1;

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

  function normalizeLoot(loot) {
    return {
      wood: Math.max(0, Math.floor(Number(loot?.wood) || 0)),
      stone: Math.max(0, Math.floor(Number(loot?.stone) || 0)),
      food: Math.max(0, Math.floor(Number(loot?.food) || 0)),
      coins: Math.max(0, Math.floor(Number(loot?.coins) || 0))
    };
  }

  function cloneTroops(troops) {
    return Object.fromEntries(Object.entries(troops || {}).map(([type, count]) => [type, Math.max(0, Math.floor(Number(count) || 0))]));
  }

  function missionCoordinates(mission, profile) {
    const own = profile?.worldPosition || { x: 0, y: 0 };
    const target = profile?.targets?.find(item => item.id === mission.targetId) || mission.targetPosition || own;
    const coordinate = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
    return {
      from: { x: coordinate(own.x), y: coordinate(own.y) },
      to: {
        x: coordinate(target.x, coordinate(mission.targetPosition?.x)),
        y: coordinate(target.y, coordinate(mission.targetPosition?.y))
      }
    };
  }

  function createMission(context, target, troops, extra = {}) {
    const profile = context.getProfile();
    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 baseTravelMs = route?.travelMs ?? fallbackTravelMs;
    const requestedTravelMs = Number(extra?.travelMs);
    const travelMs = Number.isFinite(requestedTravelMs) && requestedTravelMs > 0
      ? Math.max(1000, Math.round(requestedTravelMs))
      : baseTravelMs;
    const actualCoordinatesPerMinute = route?.distance && travelMs
      ? route.distance / (travelMs / 60000)
      : route?.coordinatesPerMinute ?? null;
    const { travelMs: _ignoredTravelMs, baseTravelMs: requestedBaseTravelMs, ...safeExtra } = extra || {};
    const startedAt = context.now();
    return {
      id: context.uid('m'),
      version: MARCH_VERSION,
      state: 'outgoing',
      targetId: target.id,
      targetName: target.name,
      targetLevelAtLaunch: target.level,
      targetPosition,
      originPosition,
      distanceCoordinates: route?.distance ?? null,
      coordinatesPerMinute: actualCoordinatesPerMinute,
      troops: cloneTroops(troops),
      startedAt,
      launchStartedAt: startedAt,
      cancelWindowEndsAt: startedAt + (globalThis.ReinosAttackCancellation?.CANCEL_WINDOW_MS || 10 * 60 * 1000),
      endAt: startedAt + travelMs,
      baseTravelMs: Number(requestedBaseTravelMs) || baseTravelMs,
      travelMs,
      ...safeExtra
    };
  }

  function finishBattleMission(context, mission, survivors, loot, win) {
    const profile = context.getProfile();
    const safeSurvivors = cloneTroops(survivors);
    const safeLoot = normalizeLoot(loot);
    const survivorTotal = totalTroops(safeSurvivors);
    const lootTotal = Object.values(safeLoot).reduce((sum, value) => sum + value, 0);

    if (survivorTotal < 1 && lootTotal < 1) {
      mission.state = 'done';
      mission.endAt = context.now();
      mission.finishedAt = context.now();
      return;
    }

    const returnMs = Math.max(4000, Math.round(Number(mission.travelPlan?.returnTravelMs) || Number(mission.travelMs) || 12000));
    const startedAt = context.now();
    mission.state = 'returning';
    mission.returning = true;
    mission.win = Boolean(win);
    mission.troops = safeSurvivors;
    mission.loot = safeLoot;
    mission.startedAt = startedAt;
    mission.endAt = startedAt + returnMs;
    mission.returnStartedAt = startedAt;
    mission.returnEndAt = mission.endAt;
  }

  function completeReturningMission(context, mission) {
    if (globalThis.ReinosAttackCancellation?.completeReturningMission?.(context, mission)) return;
    const profile = context.getProfile();
    Object.entries(cloneTroops(mission.troops)).forEach(([type, count]) => {
      profile.troops[type] = (Number(profile.troops[type]) || 0) + count;
    });
    context.addResources?.(normalizeLoot(mission.loot));
    mission.state = 'done';
    mission.finishedAt = context.now();
    const returnedTotal = totalTroops(mission.troops);
    const loot = normalizeLoot(mission.loot);
    const lootText = `madeira ${context.format(loot.wood)}, pedra ${context.format(loot.stone)}, comida ${context.format(loot.food)} e moedas ${context.format(loot.coins)}`;
    profile.reports.push({
      id: context.uid('r'),
      at: context.now(),
      title: `Exército regressou de ${mission.targetName}`,
      result: 'info',
      text: `O exército regressou ao castelo com ${context.format(returnedTotal)} unidade(s) sobrevivente(s) e trouxe ${lootText}.`
    });
    context.toast?.(`O exército regressou de ${mission.targetName}.`, 'success');
  }

  function processMissions(context, resolver) {
    const profile = context.getProfile();
    const time = context.now();
    let changed = false;

    for (const mission of [...(profile.missions || [])]) {
      if (!mission.state) mission.state = 'outgoing';

      if (mission.type === 'market_transport') {
        // Transportes do Mercado são resolvidos pelo backend para o jogador de destino
        // receber recursos mesmo com ambos os clientes fechados. O cliente apenas mostra a marcha.
        continue;
      }

      if (mission.state === 'outgoing' && mission.endAt <= time) {
        mission.state = 'resolving';
        mission.arrivedAt = time;
        resolver(mission);
        changed = true;
      }

      if (mission.state === 'returning' && mission.endAt <= time) {
        completeReturningMission(context, mission);
        changed = true;
      }
    }

    const before = profile.missions.length;
    profile.missions = profile.missions.filter(mission => mission.state !== 'done');
    if (profile.missions.length !== before) changed = true;
    return changed;
  }

  function interpolate(from, to, progress) {
    const p = Math.max(0, Math.min(1, Number(progress) || 0));
    return {
      x: from.x + (to.x - from.x) * p,
      y: from.y + (to.y - from.y) * p
    };
  }

  function marchStateLabel(mission, state) {
    if (mission.type === 'market_transport') return state === 'returning' ? 'Carroças a regressar' : 'Transporte de recursos';
    if (mission.cancelled && state === 'returning') return 'Ataque cancelado';
    return state === 'returning' ? 'Regresso' : 'Ataque';
  }

  function marchEndpoints(mission, profile, state) {
    const coords = missionCoordinates(mission, profile);
    return {
      start: state === 'returning' ? (mission.returnOriginPosition || coords.to) : coords.from,
      end: state === 'returning' ? coords.from : coords.to
    };
  }

  function routeGeometry(context, start, end) {
    const x1 = context.worldPixelX(start.x);
    const y1 = context.worldPixelY(start.y);
    const x2 = context.worldPixelX(end.x);
    const y2 = context.worldPixelY(end.y);
    const dx = x2 - x1;
    const dy = y2 - y1;
    return {
      left: x1,
      top: y1,
      width: Math.max(1, Math.hypot(dx, dy)),
      angle: Math.atan2(dy, dx) * 180 / Math.PI
    };
  }

  function routeStyle(geometry) {
    return `left:${geometry.left}px;top:${geometry.top}px;width:${geometry.width}px;transform:rotate(${geometry.angle}deg)`;
  }

  function missionProgress(mission, time) {
    return Math.max(0, Math.min(1,
      (time - Number(mission.startedAt || time)) /
      Math.max(1, Number(mission.endAt || time) - Number(mission.startedAt || time))
    ));
  }

  function renderMapMarches(context) {
    const profile = context.getProfile();
    if (!profile?.missions?.length) return '';
    const time = context.now();

    return profile.missions.filter(mission => mission.state !== 'done').map(mission => {
      const state = mission.state || 'outgoing';
      if (state === 'resolving') return '';

      const { start, end } = marchEndpoints(mission, profile, state);
      const position = interpolate(start, end, missionProgress(mission, time));
      const geometry = routeGeometry(context, start, end);
      const label = marchStateLabel(mission, state);
      const remaining = context.formatTime(Math.max(0, Number(mission.endAt || time) - time));
      const troopsTotal = mission.type === 'market_transport'
        ? Object.values(mission.resources || mission.deliveredResources || {}).reduce((sum, value) => sum + Math.max(0, Math.floor(Number(value) || 0)), 0)
        : totalTroops(mission.troops);
      const stateClass = state === 'returning' ? 'returning' : 'outgoing';
      const cancelledClass = mission.cancelled ? 'cancelled-return' : '';
      const direction = state === 'returning' ? '↶' : '➤';
      const accessible = mission.type === 'market_transport'
        ? `${label} para ${mission.targetName}. Carga ${troopsTotal}. Tempo restante ${remaining}.`
        : `${label} para ${mission.targetName}. ${troopsTotal} tropas. Tempo restante ${remaining}.`;

      return `<div class="map-march-route ${stateClass} ${cancelledClass}" data-mission-route="${context.escapeHtml(mission.id)}" style="${routeStyle(geometry)}" aria-hidden="true"></div>
        <div class="map-march-node ${stateClass} ${cancelledClass}" data-mission-id="${context.escapeHtml(mission.id)}" style="left:${context.worldPixelX(position.x)}px;top:${context.worldPixelY(position.y)}px">
          <button type="button" class="map-march-marker" data-action="mission-open" data-id="${context.escapeHtml(mission.id)}" aria-label="${context.escapeHtml(accessible)}">
            <span class="map-march-symbol" aria-hidden="true">${mission.type === 'market_transport' ? '🛒' : '⚔'}</span>
            <span class="map-march-direction" aria-hidden="true">${direction}</span>
          </button>
          <div class="map-march-tooltip" role="tooltip">
            <strong data-march-label>${context.escapeHtml(label)}</strong>
            <span data-march-target>${context.escapeHtml(mission.targetName)}</span>
            <small><b data-march-troops>${context.format(troopsTotal)}</b> ${mission.type === 'market_transport' ? 'carga' : 'tropas'} • <b data-march-time>${remaining}</b></small>
          </div>
        </div>`;
    }).join('');
  }

  function updateMapMarches(context) {
    const profile = context.getProfile();
    const routes = new Map(Array.from(document.querySelectorAll('.map-march-route[data-mission-route]'))
      .map(route => [route.dataset.missionRoute, route]));

    if (!profile?.missions?.length) {
      document.querySelectorAll('.map-march-node[data-mission-id], .map-march-route[data-mission-route]').forEach(element => element.remove());
      return;
    }

    const time = context.now();
    document.querySelectorAll('.map-march-node[data-mission-id]').forEach(node => {
      const mission = profile.missions.find(item => item.id === node.dataset.missionId);
      const route = routes.get(node.dataset.missionId);
      if (!mission || mission.state === 'done' || mission.state === 'resolving') {
        node.remove();
        route?.remove();
        return;
      }

      const state = mission.state || 'outgoing';
      const stateClass = state === 'returning' ? 'returning' : 'outgoing';
      const { start, end } = marchEndpoints(mission, profile, state);
      const position = interpolate(start, end, missionProgress(mission, time));
      const remaining = context.formatTime(Math.max(0, Number(mission.endAt || time) - time));
      const troops = mission.type === 'market_transport'
        ? Object.values(mission.resources || mission.deliveredResources || {}).reduce((sum, value) => sum + Math.max(0, Math.floor(Number(value) || 0)), 0)
        : totalTroops(mission.troops);
      const label = marchStateLabel(mission, state);

      node.style.left = `${context.worldPixelX(position.x)}px`;
      node.style.top = `${context.worldPixelY(position.y)}px`;
      node.classList.toggle('outgoing', stateClass === 'outgoing');
      node.classList.toggle('returning', stateClass === 'returning');
      node.classList.toggle('cancelled-return', Boolean(mission.cancelled));

      const marker = node.querySelector('.map-march-marker');
      if (marker) {
        marker.setAttribute('aria-label', mission.type === 'market_transport'
          ? `${label} para ${mission.targetName}. Carga ${troops}. Tempo restante ${remaining}.`
          : `${label} para ${mission.targetName}. ${troops} tropas. Tempo restante ${remaining}.`);
      }
      const direction = node.querySelector('.map-march-direction');
      if (direction) direction.textContent = state === 'returning' ? '↶' : '➤';
      const labelElement = node.querySelector('[data-march-label]');
      const targetElement = node.querySelector('[data-march-target]');
      const troopElement = node.querySelector('[data-march-troops]');
      const timeElement = node.querySelector('[data-march-time]');
      if (labelElement) labelElement.textContent = label;
      if (targetElement) targetElement.textContent = mission.targetName;
      if (troopElement) troopElement.textContent = context.format(troops);
      if (timeElement) timeElement.textContent = remaining;

      if (route) {
        const geometry = routeGeometry(context, start, end);
        route.style.left = `${geometry.left}px`;
        route.style.top = `${geometry.top}px`;
        route.style.width = `${geometry.width}px`;
        route.style.transform = `rotate(${geometry.angle}deg)`;
        route.classList.toggle('outgoing', stateClass === 'outgoing');
        route.classList.toggle('returning', stateClass === 'returning');
        route.classList.toggle('cancelled-return', Boolean(mission.cancelled));
      }
    });

    routes.forEach((route, missionId) => {
      if (!profile.missions.some(mission => mission.id === missionId && !['done', 'resolving'].includes(mission.state))) {
        route.remove();
      }
    });
  }

  globalThis.ReinosWorldMarches = Object.freeze({
    MARCH_VERSION,
    createMission,
    finishBattleMission,
    processMissions,
    renderMapMarches,
    updateMapMarches,
    totalTroops
  });
})();
