'use strict';

/**
 * Distribuição automática baseada no último relatório de espionagem.
 *
 * Este módulo apenas preenche o rascunho do planeador. Não envia o ataque,
 * não consome tropas/ferramentas e não resolve batalhas.
 */
(() => {
  const VERSION = 1;

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

  function totalUnits(units) {
    return Object.values(units || {}).reduce((sum, count) => sum + safeNumber(count), 0);
  }

  function latestSpyReport(draft, target) {
    const reports = draft?.context?.getProfile?.()?.reports || [];
    return [...reports]
      .filter(report => report?.spyData && report.targetId === draft.targetId)
      .sort((a, b) => Number(b.at || 0) - Number(a.at || 0))
      .find(report => {
        if (draft.targetType === 'falken') return safeNumber(report.targetLevel) === safeNumber(target?.level);
        return true;
      }) || null;
  }

  function sectorDefenseScore(sector, troopMeta) {
    const unitPower = Object.entries(sector?.units || {}).reduce((sum, [type, count]) => {
      return sum + safeNumber(count) * Math.max(1, Number(troopMeta?.[type]?.defense) || 15);
    }, 0);
    const fortification = 1 + (
      safeNumber(sector?.wallBonus) +
      safeNumber(sector?.gateBonus) * 0.7 +
      safeNumber(sector?.rangedBonus) * 0.45
    ) / 100;
    return Math.max(1, unitPower * fortification);
  }

  function troopSectorWeight(type, sector, baseScore) {
    const units = sector?.units || {};
    const ranged = safeNumber(units.archer);
    const melee = safeNumber(units.spearman) + safeNumber(units.swordsman) + safeNumber(units.knight);
    const total = Math.max(1, ranged + melee);
    const rangedShare = ranged / total;
    const meleeShare = melee / total;

    if (type === 'archer') return baseScore * (1 + meleeShare * 0.38);
    if (type === 'swordsman') return baseScore * (1 + rangedShare * 0.42);
    if (type === 'knight') return baseScore * (1 + rangedShare * 0.28 + meleeShare * 0.12);
    return baseScore * (1 + meleeShare * 0.08);
  }

  function distributeInteger(total, items, weightFn) {
    const result = Object.fromEntries(items.map(item => [item.id, 0]));
    let remaining = safeNumber(total);
    if (!remaining || !items.length) return result;

    const weights = items.map(item => Math.max(0.0001, Number(weightFn(item)) || 0.0001));
    const weightTotal = weights.reduce((sum, value) => sum + value, 0);
    const fractions = [];

    items.forEach((item, index) => {
      const exact = remaining * weights[index] / weightTotal;
      const base = Math.floor(exact);
      result[item.id] = base;
      fractions.push({ id: item.id, fraction: exact - base, weight: weights[index] });
    });

    let unassigned = remaining - Object.values(result).reduce((sum, value) => sum + value, 0);
    fractions.sort((a, b) => b.fraction - a.fraction || b.weight - a.weight);
    for (let index = 0; unassigned > 0; index = (index + 1) % fractions.length) {
      result[fractions[index].id] += 1;
      unassigned -= 1;
    }
    return result;
  }

  function toolsAssignedOutsideWave(draft, type, waveIndex) {
    let total = 0;
    (draft?.waves || []).forEach((wave, index) => {
      if (index === waveIndex) return;
      Object.values(wave.flanks || {}).forEach(flank => { total += safeNumber(flank?.tools?.[type]); });
    });
    return total;
  }

  function allocateTool(type, available, sectors, wave, needsBySector, bonusField) {
    let remaining = safeNumber(available);
    const assigned = Object.fromEntries(sectors.map(sector => [sector.id, 0]));
    while (remaining > 0) {
      const candidates = sectors
        .filter(sector => assigned[sector.id] < safeNumber(needsBySector[sector.id]?.[type]))
        .sort((a, b) => {
          const aRemaining = safeNumber(needsBySector[a.id]?.[type]) - assigned[a.id];
          const bRemaining = safeNumber(needsBySector[b.id]?.[type]) - assigned[b.id];
          const aPriority = aRemaining * (1 + safeNumber(a[bonusField]) / 100);
          const bPriority = bRemaining * (1 + safeNumber(b[bonusField]) / 100);
          return bPriority - aPriority;
        });
      if (!candidates.length) break;
      const selected = candidates[0];
      assigned[selected.id] += 1;
      remaining -= 1;
    }

    sectors.forEach(sector => {
      wave.flanks[sector.id].tools[type] = assigned[sector.id];
    });
    return { assigned, unused: remaining };
  }

  function fillTools(draft, waveIndex, sectors) {
    const profile = draft.context.getProfile();
    globalThis.ReinosSiegeTools?.ensureState?.(profile);
    const wave = draft.waves[waveIndex];
    const needsBySector = Object.fromEntries(sectors.map(sector => [
      sector.id,
      globalThis.ReinosFalkenDefenseTools?.attackToolNeeds?.(sector) || {
        ladder: Math.ceil(Math.min(100, safeNumber(sector.wallBonus)) / 10),
        ram: sector.id === 'center' ? Math.ceil(Math.min(100, safeNumber(sector.gateBonus)) / 10) : 0,
        mantlet: Math.ceil(Math.min(100, safeNumber(sector.rangedBonus)) / 5)
      }
    ]));

    const stock = profile.siegeTools || {};
    const available = type => Math.max(0, safeNumber(stock[type]) - toolsAssignedOutsideWave(draft, type, waveIndex));
    const results = {
      ladder: allocateTool('ladder', available('ladder'), sectors, wave, needsBySector, 'wallBonus'),
      mantlet: allocateTool('mantlet', available('mantlet'), sectors, wave, needsBySector, 'rangedBonus')
    };

    const center = sectors.find(sector => sector.id === 'center');
    if (center) {
      results.ram = allocateTool('ram', available('ram'), [center], wave, needsBySector, 'gateBonus');
    } else {
      results.ram = { assigned: {}, unused: available('ram') };
    }

    const shortages = [];
    for (const type of ['ladder', 'ram', 'mantlet']) {
      const needed = sectors.reduce((sum, sector) => sum + safeNumber(needsBySector[sector.id]?.[type]), 0);
      const placed = Object.values(results[type]?.assigned || {}).reduce((sum, count) => sum + safeNumber(count), 0);
      if (placed < needed) shortages.push(type);
    }
    return shortages;
  }

  function fill(options) {
    const { draft, waveIndex, flanks, assignedByType } = options || {};
    if (!draft?.waves?.[waveIndex]) return false;
    const context = draft.context;
    const target = context.getAttackTarget?.(draft.targetId, draft.targetType) || draft.targetSnapshot;
    const report = latestSpyReport(draft, target);
    if (!report?.spyData?.sectors?.length) {
      context.toast('Não existe um relatório de espionagem atual deste alvo. Foi usada a distribuição normal.', 'error');
      return false;
    }

    const wave = draft.waves[waveIndex];
    const enabledIds = new Set((flanks || []).filter(flank => wave.flanks?.[flank.id]?.enabled).map(flank => flank.id));
    const sectors = report.spyData.sectors.filter(sector => enabledIds.has(sector.id));
    if (!sectors.length) return false;

    const troopMeta = context.getTroopsMeta();
    const scoreBySector = Object.fromEntries(sectors.map(sector => [sector.id, sectorDefenseScore(sector, troopMeta)]));
    const profile = context.getProfile();

    Object.keys(troopMeta).forEach(type => {
      const available = Math.max(0, safeNumber(profile.troops?.[type]) - safeNumber(assignedByType(type)));
      const distribution = distributeInteger(available, sectors, sector => troopSectorWeight(type, sector, scoreBySector[sector.id]));
      sectors.forEach(sector => { wave.flanks[sector.id].troops[type] = distribution[sector.id]; });
    });

    const shortages = fillTools(draft, waveIndex, sectors);
    const toolNames = { ladder: 'escadas', ram: 'aríetes', mantlet: 'manteletes' };
    const shortageText = shortages.length
      ? ` Não existem ${shortages.map(type => toolNames[type]).join(', ')} suficientes para neutralizar toda a defesa.`
      : ' As ferramentas necessárias foram colocadas automaticamente.';
    context.toast(`Formação distribuída segundo a espionagem de ${target?.name || 'alvo'}.${shortageText}`, shortages.length ? 'error' : 'success');
    return true;
  }

  globalThis.ReinosIntelligentAutofill = Object.freeze({
    VERSION,
    latestSpyReport,
    sectorDefenseScore,
    fill
  });
})();
