'use strict';

/**
 * População modular de Barões Falken no mapa mundial.
 *
 * A distribuição combina:
 * - uma população global determinística, espalhada por todo o reino;
 * - pequenos grupos irregulares em redor dos castelos dos jogadores.
 *
 * Os grupos próximos usam IDs estáveis e são partilhados no mapa. O nível,
 * as vitórias e o tempo de espera continuam guardados individualmente no
 * perfil de cada jogador, tal como acontece com os restantes Barões Falken.
 */
(() => {
  const VERSION = 3;
  const TARGET_COUNT = 480;
  const NEARBY_TARGETS_PER_CASTLE = 8;
  const MIN_COORD = -1000;
  const MAX_COORD = 1000;
  const MARGIN = 45;
  const MIN_CASTLE_DISTANCE = 22;
  const MAX_CASTLE_DISTANCE = 48;

  const LEGACY_POSITIONS = [
    { x: -640, y: -499 }, { x: -299, y: 241 }, { x: 161, y: -600 }, { x: 542, y: -39 },
    { x: -499, y: 642 }, { x: -39, y: 502 }, { x: 441, y: 682 }, { x: 762, y: -660 }
  ];

  const NEARBY_PATTERN = Object.freeze([
    Object.freeze({ angle: 0.08, radius: 24 }),
    Object.freeze({ angle: 0.83, radius: 34 }),
    Object.freeze({ angle: 1.61, radius: 27 }),
    Object.freeze({ angle: 2.43, radius: 43 }),
    Object.freeze({ angle: 3.18, radius: 25 }),
    Object.freeze({ angle: 4.02, radius: 37 }),
    Object.freeze({ angle: 4.91, radius: 29 }),
    Object.freeze({ angle: 5.69, radius: 47 })
  ]);

  function clamp(value, min, max) {
    return Math.max(min, Math.min(max, value));
  }

  function normalizeCoordinate(value, fallback = 0) {
    const parsed = Number(value);
    return clamp(Math.round(Number.isFinite(parsed) ? parsed : fallback), MIN_COORD, MAX_COORD);
  }

  function hashText(text) {
    let hash = 2166136261;
    for (const char of String(text || 'castelo')) {
      hash ^= char.charCodeAt(0);
      hash = Math.imul(hash, 16777619);
    }
    return hash >>> 0;
  }

  function seeded(index, salt) {
    let value = Math.imul(index + 1, 1103515245) + Math.imul(salt + 17, 12345);
    value ^= value >>> 16;
    value = Math.imul(value, 2246822519);
    value ^= value >>> 13;
    return (value >>> 0) / 4294967295;
  }

  function generateGlobalPositions() {
    const positions = LEGACY_POSITIONS.map(position => ({ ...position }));
    const columns = 24;
    const rows = 20;
    const spanX = MAX_COORD - MIN_COORD - MARGIN * 2;
    const spanY = MAX_COORD - MIN_COORD - MARGIN * 2;
    const stepX = spanX / (columns - 1);
    const stepY = spanY / (rows - 1);

    for (let row = 0; row < rows && positions.length < TARGET_COUNT; row += 1) {
      for (let column = 0; column < columns && positions.length < TARGET_COUNT; column += 1) {
        const seedIndex = row * columns + column;
        const jitterX = (seeded(seedIndex, 11) - 0.5) * stepX * 0.58;
        const jitterY = (seeded(seedIndex, 29) - 0.5) * stepY * 0.58;
        let x = Math.round(MIN_COORD + MARGIN + column * stepX + jitterX);
        let y = Math.round(MIN_COORD + MARGIN + row * stepY + jitterY);

        x = clamp(x, MIN_COORD + MARGIN, MAX_COORD - MARGIN);
        y = clamp(y, MIN_COORD + MARGIN, MAX_COORD - MARGIN);

        const duplicate = positions.some(position => Math.hypot(position.x - x, position.y - y) < 20);
        if (!duplicate) positions.push({ x, y });
      }
    }

    return positions.slice(0, TARGET_COUNT).map((position, index) => Object.freeze({
      id: `npc_${index + 1}`,
      x: position.x,
      y: position.y,
      populationType: 'global'
    }));
  }

  function normalizeCastles(castles) {
    const seen = new Set();
    return (Array.isArray(castles) ? castles : [])
      .map((castle, index) => {
        const key = String(castle?.id || castle?.userId || `castle_${index + 1}`);
        return {
          id: key,
          x: normalizeCoordinate(castle?.x ?? castle?.worldPosition?.x),
          y: normalizeCoordinate(castle?.y ?? castle?.worldPosition?.y)
        };
      })
      .filter(castle => {
        if (seen.has(castle.id)) return false;
        seen.add(castle.id);
        return true;
      })
      .sort((a, b) => a.id.localeCompare(b.id));
  }

  function nearbyPositionsForCastle(castle) {
    const castleHash = hashText(castle.id);
    const rotation = (castleHash % 6283) / 1000;
    const mirror = castleHash & 1 ? 1 : -1;
    const idRoot = castleHash.toString(36);

    return NEARBY_PATTERN.slice(0, NEARBY_TARGETS_PER_CASTLE).map((pattern, index) => {
      const radiusVariation = ((hashText(`${castle.id}:radius:${index}`) % 9) - 4);
      const radius = clamp(pattern.radius + radiusVariation, MIN_CASTLE_DISTANCE, MAX_CASTLE_DISTANCE);
      const angleVariation = (((hashText(`${castle.id}:angle:${index}`) % 21) - 10) / 100);
      const angle = rotation + mirror * pattern.angle + angleVariation;
      const x = normalizeCoordinate(castle.x + Math.cos(angle) * radius, castle.x);
      const y = normalizeCoordinate(castle.y + Math.sin(angle) * radius, castle.y);

      return Object.freeze({
        id: `npc_near_${idRoot}_${index + 1}`,
        x,
        y,
        populationType: 'castle-nearby',
        castleId: castle.id
      });
    });
  }

  const POSITIONS = Object.freeze(generateGlobalPositions());

  function getPositionsForCastles(castles) {
    const normalizedCastles = normalizeCastles(castles);
    const nearby = normalizedCastles.flatMap(nearbyPositionsForCastle);
    return Object.freeze([...POSITIONS, ...nearby]);
  }

  globalThis.ReinosFalkenPopulation = Object.freeze({
    VERSION,
    TARGET_COUNT,
    NEARBY_TARGETS_PER_CASTLE,
    MIN_CASTLE_DISTANCE,
    MAX_CASTLE_DISTANCE,
    POSITIONS,
    getPositions: () => POSITIONS,
    getPositionsForCastles,
    nearbyPositionsForCastle
  });
})();
