'use strict';

/**
 * Sistema modular de fome e deserção das tropas.
 *
 * Regras:
 * - a deserção só é verificada quando a reserva de comida chega a zero;
 * - permanecem apenas as unidades que a produção bruta de comida consegue sustentar;
 * - todas as tropas pertencentes ao jogador contam, incluindo marchas de ida e regresso;
 * - a remoção é proporcional entre o castelo e as marchas ativas;
 * - o jogador recebe um aviso e um relatório detalhado.
 */
(() => {
  const VERSION = 1;
  const FOOD_EPSILON = 0.0001;

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

  function safeRate(value) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
  }

  function activeMission(mission) {
    return Boolean(mission) && typeof mission === 'object' && mission.state !== 'done';
  }

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

  function collectPools(profile) {
    const pools = [];

    Object.keys(profile?.troops || {}).forEach(type => {
      const count = safeCount(profile.troops[type]);
      if (count > 0) {
        pools.push({
          source: 'castle',
          sourceId: 'castle',
          sourceName: 'Castelo',
          type,
          count,
          troops: profile.troops
        });
      }
    });

    (Array.isArray(profile?.missions) ? profile.missions : [])
      .filter(activeMission)
      .forEach(mission => {
        Object.keys(mission.troops || {}).forEach(type => {
          const count = safeCount(mission.troops[type]);
          if (count > 0) {
            pools.push({
              source: 'mission',
              sourceId: mission.id || 'mission',
              sourceName: mission.targetName || 'Marcha',
              type,
              count,
              troops: mission.troops,
              mission
            });
          }
        });
      });

    return pools;
  }

  function proportionalRemovals(pools, amount) {
    const total = pools.reduce((sum, entry) => sum + entry.count, 0);
    const target = Math.min(safeCount(amount), total);
    if (target < 1 || total < 1) return new Map();

    const allocations = pools.map((entry, index) => {
      const exact = target * entry.count / total;
      const base = Math.min(entry.count, Math.floor(exact));
      return { entry, index, remove: base, fraction: exact - base };
    });

    let assigned = allocations.reduce((sum, item) => sum + item.remove, 0);
    allocations.sort((a, b) => b.fraction - a.fraction || b.entry.count - a.entry.count || a.index - b.index);

    while (assigned < target) {
      let progressed = false;
      for (const item of allocations) {
        if (assigned >= target) break;
        if (item.remove >= item.entry.count) continue;
        item.remove += 1;
        assigned += 1;
        progressed = true;
      }
      if (!progressed) break;
    }

    return new Map(allocations.map(item => [item.entry, item.remove]));
  }

  function toolMapFromMission(mission) {
    if (mission?.cancelled) return mission.cancelledTools || {};
    if ((mission?.state || 'outgoing') !== 'outgoing') return {};
    const summarize = globalThis.ReinosAttackTools?.summarizePlan;
    if (typeof summarize === 'function' && Array.isArray(mission?.attackPlan?.waves)) {
      return summarize(mission.attackPlan.waves) || {};
    }
    return {};
  }

  function resolveEmptyMissions(profile, currentTime, summary) {
    for (const mission of (Array.isArray(profile?.missions) ? profile.missions : [])) {
      if (!activeMission(mission) || troopTotal(mission.troops) > 0) continue;

      mission.state = 'done';
      mission.finishedAt = currentTime;
      mission.starvedOut = true;
      mission.loot = { wood: 0, stone: 0, food: 0, coins: 0 };
      summary.lostMissions.push(mission.targetName || 'Alvo desconhecido');

      const tools = toolMapFromMission(mission);
      const toolsLost = Object.values(tools || {}).reduce((sum, value) => sum + safeCount(value), 0);
      summary.toolsLost += toolsLost;
    }

    profile.missions = (Array.isArray(profile?.missions) ? profile.missions : []).filter(mission => mission.state !== 'done');
  }

  function troopName(type, definitions) {
    return definitions?.[type]?.name || type;
  }

  function breakdownText(summary, definitions, format) {
    const parts = Object.entries(summary.byType)
      .filter(([, count]) => count > 0)
      .map(([type, count]) => `${format(count)} ${troopName(type, definitions)}`);
    return parts.length ? parts.join(', ') : `${format(summary.total)} tropas`;
  }

  function ensureState(profile) {
    if (!profile || typeof profile !== 'object') return;
    if (!profile.foodDesertion || typeof profile.foodDesertion !== 'object') {
      profile.foodDesertion = {
        version: VERSION,
        totalDeserted: 0,
        events: 0,
        lastEventAt: 0
      };
    }
    profile.foodDesertion.version = VERSION;
    profile.foodDesertion.totalDeserted = safeCount(profile.foodDesertion.totalDeserted);
    profile.foodDesertion.events = safeCount(profile.foodDesertion.events);
    profile.foodDesertion.lastEventAt = Number(profile.foodDesertion.lastEventAt) || 0;
  }

  function process(options = {}) {
    const profile = options.profile;
    if (!profile?.resources || !profile?.troops) return { changed: false };
    ensureState(profile);

    const foodStored = Math.max(0, Number(profile.resources.food) || 0);
    if (foodStored > FOOD_EPSILON) return { changed: false };

    const upkeep = globalThis.ReinosTroopUpkeep;
    const foodPerUnit = Math.max(0.0001, Number(upkeep?.FOOD_PER_UNIT_PER_HOUR) || 2);
    const grossFoodPerHour = safeRate(options.grossFoodPerHour);
    const sustainableUnits = Math.max(0, Math.floor(grossFoodPerHour / foodPerUnit));
    const pools = collectPools(profile);
    const totalUnits = pools.reduce((sum, entry) => sum + entry.count, 0);
    const excess = Math.max(0, totalUnits - sustainableUnits);

    if (excess < 1) return {
      changed: false,
      grossFoodPerHour,
      sustainableUnits,
      totalUnits
    };

    const removals = proportionalRemovals(pools, excess);
    const summary = {
      total: 0,
      castle: 0,
      marches: 0,
      byType: {},
      byMission: {},
      lostMissions: [],
      toolsLost: 0,
      sustainableUnits,
      grossFoodPerHour
    };

    for (const entry of pools) {
      const remove = Math.min(entry.count, safeCount(removals.get(entry)));
      if (remove < 1) continue;
      entry.troops[entry.type] = Math.max(0, safeCount(entry.troops[entry.type]) - remove);
      summary.total += remove;
      summary.byType[entry.type] = (summary.byType[entry.type] || 0) + remove;
      if (entry.source === 'castle') {
        summary.castle += remove;
      } else {
        summary.marches += remove;
        summary.byMission[entry.sourceName] = (summary.byMission[entry.sourceName] || 0) + remove;
      }
    }

    const currentTime = Number(options.currentTime) || Date.now();
    resolveEmptyMissions(profile, currentTime, summary);

    profile.resources.food = 0;
    profile.foodDesertion.totalDeserted += summary.total;
    profile.foodDesertion.events += 1;
    profile.foodDesertion.lastEventAt = currentTime;

    const format = typeof options.format === 'function' ? options.format : value => String(Math.floor(Number(value) || 0));
    const uid = typeof options.uid === 'function' ? options.uid : prefix => `${prefix}_${currentTime}_${Math.random().toString(36).slice(2, 8)}`;
    const definitions = options.troopDefinitions || {};
    const details = breakdownText(summary, definitions, format);
    const locationDetails = [
      summary.castle > 0 ? `${format(summary.castle)} no castelo` : '',
      summary.marches > 0 ? `${format(summary.marches)} em marcha` : ''
    ].filter(Boolean).join(' e ');
    const missionLoss = summary.lostMissions.length
      ? ` ${summary.lostMissions.length === 1 ? 'Uma marcha ficou sem soldados e foi terminada' : `${summary.lostMissions.length} marchas ficaram sem soldados e foram terminadas`}.`
      : '';
    const toolLoss = summary.toolsLost > 0
      ? ` Foram também perdidas ${format(summary.toolsLost)} ferramenta(s) que seguiam sem escolta.`
      : '';

    profile.reports = Array.isArray(profile.reports) ? profile.reports : [];
    profile.reports.push({
      id: uid('r'),
      at: currentTime,
      title: 'Fome no reino — soldados desertaram',
      result: 'loss',
      starvationData: {
        version: VERSION,
        total: summary.total,
        castle: summary.castle,
        marches: summary.marches,
        byType: { ...summary.byType },
        sustainableUnits,
        grossFoodPerHour,
        lostMissions: [...summary.lostMissions],
        toolsLost: summary.toolsLost
      },
      text: `A reserva de comida chegou a zero. Desertaram ${format(summary.total)} soldados (${details}; ${locationDetails || 'no reino'}). A produção atual de ${format(grossFoodPerHour)} comida por hora consegue sustentar ${format(sustainableUnits)} soldados.${missionLoss}${toolLoss}`
    });

    options.toast?.(`Fome! ${format(summary.total)} soldados desertaram por falta de comida.`, 'error');
    return { changed: true, ...summary };
  }

  globalThis.ReinosFoodDesertion = Object.freeze({
    VERSION,
    ensureState,
    process,
    troopTotal,
    collectPools
  });
})();
