'use strict';

(() => {
  const VERSION = 1;
  const GRID_SIZE = 60;
  const MAX_EXPANSIONS = 16;
  const INITIAL_REGION = Object.freeze({ id: 'center', name: 'Castelo inicial', x: 20, y: 20, w: 20, h: 20, initial: true });

  const PLOTS = Object.freeze([
    ...Array.from({ length: 6 }, (_, i) => ({ id: `north-${i + 1}`, name: `Expansão norte ${i + 1}`, x: i * 10, y: 0, w: 10, h: 20, side: 'north', tower: { x: i * 10 + 5, y: 0 } })),
    { id: 'west-1', name: 'Expansão oeste 1', x: 0, y: 20, w: 10, h: 20, side: 'west', tower: { x: 0, y: 30 } },
    { id: 'west-2', name: 'Expansão oeste 2', x: 10, y: 20, w: 10, h: 20, side: 'west', tower: { x: 0, y: 35 } },
    { id: 'east-1', name: 'Expansão este 1', x: 40, y: 20, w: 10, h: 20, side: 'east', tower: { x: 60, y: 25 } },
    { id: 'east-2', name: 'Expansão este 2', x: 50, y: 20, w: 10, h: 20, side: 'east', tower: { x: 60, y: 35 } },
    ...Array.from({ length: 6 }, (_, i) => ({ id: `south-${i + 1}`, name: `Expansão sul ${i + 1}`, x: i * 10, y: 40, w: 10, h: 20, side: 'south', tower: { x: i * 10 + 5, y: 60 } }))
  ]);

  const BY_ID = Object.freeze(Object.fromEntries(PLOTS.map(plot => [plot.id, plot])));

  function normalizeState(profile) {
    if (!profile) return false;
    if (!profile.castleState || typeof profile.castleState !== 'object') profile.castleState = {};
    const state = profile.castleState;
    let changed = false;

    if (!Array.isArray(state.unlockedExpansionIds)) {
      // O antigo sistema chegava apenas a 20 × 20. No novo sistema essa área passa
      // a ser o castelo inicial, portanto não são consumidas expansões adicionais.
      state.unlockedExpansionIds = [];
      changed = true;
    }

    const clean = [...new Set(state.unlockedExpansionIds.map(String).filter(id => BY_ID[id]))].slice(0, MAX_EXPANSIONS);
    if (clean.length !== state.unlockedExpansionIds.length || clean.some((id, index) => id !== state.unlockedExpansionIds[index])) changed = true;
    state.unlockedExpansionIds = clean;
    state.expansionLevel = clean.length;
    state.expansionSystemVersion = VERSION;
    return changed;
  }

  function legacyOrder() {
    return ['north-3', 'south-3', 'west-2', 'east-1', 'north-4', 'south-4', 'west-1', 'east-2', 'north-2', 'south-2', 'north-5', 'south-5', 'north-1', 'south-1', 'north-6', 'south-6'];
  }

  function purchasedIds(profile) {
    normalizeState(profile);
    return new Set(profile.castleState.unlockedExpansionIds);
  }

  function regions(profile) {
    const ids = purchasedIds(profile);
    return [INITIAL_REGION, ...PLOTS.filter(plot => ids.has(plot.id))];
  }

  function plotById(id) {
    return BY_ID[String(id)] || null;
  }

  function contains(region, x, y) {
    return x >= region.x && y >= region.y && x < region.x + region.w && y < region.y + region.h;
  }

  function isCellUnlocked(profile, x, y) {
    if (x < 0 || y < 0 || x >= GRID_SIZE || y >= GRID_SIZE) return false;
    return regions(profile).some(region => contains(region, x, y));
  }

  function isRectUnlocked(profile, x, y, w, h) {
    for (let yy = y; yy < y + h; yy++) {
      for (let xx = x; xx < x + w; xx++) {
        if (!isCellUnlocked(profile, xx, yy)) return false;
      }
    }
    return true;
  }

  function overlapLength(a1, a2, b1, b2) {
    return Math.max(0, Math.min(a2, b2) - Math.max(a1, b1));
  }

  function adjacent(a, b) {
    const horizontalTouch = (a.x + a.w === b.x || b.x + b.w === a.x) && overlapLength(a.y, a.y + a.h, b.y, b.y + b.h) > 0;
    const verticalTouch = (a.y + a.h === b.y || b.y + b.h === a.y) && overlapLength(a.x, a.x + a.w, b.x, b.x + b.w) > 0;
    return horizontalTouch || verticalTouch;
  }

  function availablePlots(profile) {
    const unlocked = regions(profile);
    const owned = purchasedIds(profile);
    return PLOTS.filter(plot => !owned.has(plot.id) && unlocked.some(region => adjacent(plot, region)));
  }

  function isAvailable(profile, id) {
    return availablePlots(profile).some(plot => plot.id === String(id));
  }

  function cost(profile) {
    normalizeState(profile);
    const next = profile.castleState.unlockedExpansionIds.length + 1;
    const factor = Math.pow(1.32, next - 1);
    return {
      resources: {
        wood: Math.round(2400 * factor / 10) * 10,
        stone: Math.round(2900 * factor / 10) * 10
      },
      crowns: { gems: Math.max(90, Math.round(95 * Math.pow(1.24, next - 1))) }
    };
  }

  function purchase(profile, id) {
    normalizeState(profile);
    id = String(id);
    if (!BY_ID[id] || profile.castleState.unlockedExpansionIds.includes(id) || !isAvailable(profile, id)) return false;
    profile.castleState.unlockedExpansionIds.push(id);
    profile.castleState.expansionLevel = profile.castleState.unlockedExpansionIds.length;
    return true;
  }

  function unlockedArea(profile) {
    return 400 + purchasedIds(profile).size * 200;
  }

  function bounds(profile) {
    const list = regions(profile);
    const minX = Math.min(...list.map(region => region.x));
    const minY = Math.min(...list.map(region => region.y));
    const maxX = Math.max(...list.map(region => region.x + region.w - 1));
    const maxY = Math.max(...list.map(region => region.y + region.h - 1));
    return {
      min: Math.min(minX, minY),
      max: Math.max(maxX, maxY),
      minX,
      minY,
      maxX,
      maxY,
      width: maxX - minX + 1,
      height: maxY - minY + 1,
      size: Math.max(maxX - minX + 1, maxY - minY + 1),
      area: unlockedArea(profile)
    };
  }

  function cells(profile) {
    const result = [];
    for (let y = 0; y < GRID_SIZE; y++) {
      for (let x = 0; x < GRID_SIZE; x++) {
        if (isCellUnlocked(profile, x, y)) result.push({ x, y });
      }
    }
    return result;
  }

  function boundaryEdges(profile) {
    const result = [];
    for (const { x, y } of cells(profile)) {
      if (!isCellUnlocked(profile, x, y - 1)) result.push({ x, y: y - 0.55, side: 'top', orientation: 'x-back' });
      if (!isCellUnlocked(profile, x, y + 1)) result.push({ x, y: y + 0.55, side: 'bottom', orientation: 'x-front' });
      if (!isCellUnlocked(profile, x - 1, y)) result.push({ x: x - 0.55, y, side: 'left', orientation: 'y-back' });
      if (!isCellUnlocked(profile, x + 1, y)) result.push({ x: x + 0.55, y, side: 'right', orientation: 'y-front' });
    }
    return result;
  }

  function gateEdge(profile) {
    const south = boundaryEdges(profile).filter(edge => edge.side === 'bottom');
    if (!south.length) return null;
    const center = 29.5;
    return south.sort((a, b) => b.y - a.y || Math.abs(a.x - center) - Math.abs(b.x - center))[0];
  }

  function isBoundaryPoint(profile, point) {
    const around = [
      isCellUnlocked(profile, point.x - 1, point.y - 1),
      isCellUnlocked(profile, point.x, point.y - 1),
      isCellUnlocked(profile, point.x - 1, point.y),
      isCellUnlocked(profile, point.x, point.y)
    ];
    return around.some(Boolean) && around.some(value => !value);
  }

  function convexCorners(profile) {
    const points = [];
    for (let y = 0; y <= GRID_SIZE; y++) {
      for (let x = 0; x <= GRID_SIZE; x++) {
        const quadrants = [
          isCellUnlocked(profile, x - 1, y - 1),
          isCellUnlocked(profile, x, y - 1),
          isCellUnlocked(profile, x - 1, y),
          isCellUnlocked(profile, x, y)
        ];
        if (quadrants.filter(Boolean).length === 1) points.push({ x, y, kind: 'corner' });
      }
    }
    return points;
  }

  function towerPoints(profile) {
    const points = convexCorners(profile);
    const owned = purchasedIds(profile);
    const currentBounds = bounds(profile);
    for (const plot of PLOTS) {
      if (!owned.has(plot.id)) continue;
      let point;
      if (plot.side === 'north') point = { x: plot.x + plot.w / 2, y: currentBounds.minY };
      if (plot.side === 'south') point = { x: plot.x + plot.w / 2, y: currentBounds.maxY + 1 };
      if (plot.side === 'west') point = { x: currentBounds.minX, y: plot.id === 'west-1' ? 25 : 35 };
      if (plot.side === 'east') point = { x: currentBounds.maxX + 1, y: plot.id === 'east-1' ? 25 : 35 };
      if (point && isBoundaryPoint(profile, point)) points.push({ ...point, kind: 'expansion', plotId: plot.id });
    }
    const unique = new Map();
    for (const point of points) unique.set(`${point.x}:${point.y}`, point);
    return [...unique.values()];
  }

  function polygonForRegion(region, project) {
    const points = [
      project(region.x, region.y),
      project(region.x + region.w, region.y),
      project(region.x + region.w, region.y + region.h),
      project(region.x, region.y + region.h)
    ];
    const minX = Math.min(...points.map(point => point.x));
    const minY = Math.min(...points.map(point => point.y));
    const maxX = Math.max(...points.map(point => point.x));
    const maxY = Math.max(...points.map(point => point.y));
    const width = Math.max(1, maxX - minX);
    const height = Math.max(1, maxY - minY);
    const polygon = points.map(point => `${((point.x - minX) / width) * 100}% ${((point.y - minY) / height) * 100}%`).join(',');
    return { minX, minY, width, height, polygon };
  }

  function groundHtml(profile, project) {
    return regions(profile).map((region, index) => {
      const shape = polygonForRegion(region, project);
      return `<span class="castle-expansion-ground ${region.initial ? 'is-initial' : 'is-purchased'}" data-expansion-ground="${region.id}" style="left:${shape.minX}px;top:${shape.minY}px;width:${shape.width}px;height:${shape.height}px;clip-path:polygon(${shape.polygon});z-index:${68 + index}"><i></i></span>`;
    }).join('');
  }

  function mapHtml(profile) {
    const owned = purchasedIds(profile);
    const available = new Set(availablePlots(profile).map(plot => plot.id));
    const cell = plot => {
      const state = owned.has(plot.id) ? 'purchased' : available.has(plot.id) ? 'available' : 'locked';
      const label = owned.has(plot.id) ? 'Adquirida' : available.has(plot.id) ? 'Disponível' : 'Bloqueada';
      return `<button type="button" class="castle-expansion-plot ${state}" data-action="castle-expansion-select" data-expansion-id="${plot.id}" ${state === 'available' ? '' : 'disabled'} title="${plot.name}"><b>${plot.side === 'north' ? 'N' : plot.side === 'south' ? 'S' : plot.side === 'west' ? 'O' : 'E'}</b><span>10×20</span><em>${label}</em></button>`;
    };
    const top = PLOTS.filter(plot => plot.side === 'north').map(cell).join('');
    const bottom = PLOTS.filter(plot => plot.side === 'south').map(cell).join('');
    const west = PLOTS.filter(plot => plot.side === 'west').map(cell).join('');
    const east = PLOTS.filter(plot => plot.side === 'east').map(cell).join('');
    return `<div class="castle-expansion-map">
      <div class="expansion-row expansion-top">${top}</div>
      <div class="expansion-middle"><div class="expansion-side">${west}</div><div class="expansion-center"><strong>Castelo inicial</strong><span>20 × 20</span><small>400 quadrados</small></div><div class="expansion-side">${east}</div></div>
      <div class="expansion-row expansion-bottom">${bottom}</div>
    </div>`;
  }

  function modalHtml(profile, ctx) {
    normalizeState(profile);
    const count = profile.castleState.unlockedExpansionIds.length;
    return `<div class="castle-expansion-panel">
      <div class="castle-expansion-summary"><div><span>Expansões adquiridas</span><strong>${count} / ${MAX_EXPANSIONS}</strong></div><div><span>Área utilizável</span><strong>${ctx.format(unlockedArea(profile))} / 3 600</strong></div><div><span>Dimensão máxima</span><strong>60 × 60</strong></div><div><span>Nova torre</span><strong>1 por expansão</strong></div></div>
      <p>Cada expansão desbloqueia imediatamente uma área de <b>10 × 20 quadrados</b> e acrescenta um novo ponto de torre à muralha. Seleciona uma das áreas verdes adjacentes ao teu castelo.</p>
      ${mapHtml(profile)}
      <div class="castle-expansion-note">A compra é instantânea e pode ser paga com madeira e pedra ou com coroas.</div>
    </div>`;
  }

  function purchaseHtml(profile, id, ctx) {
    const plot = plotById(id);
    if (!plot || !isAvailable(profile, id)) return '';
    const prices = cost(profile);
    return `<div class="castle-expansion-purchase">
      <div class="castle-expansion-selected"><strong>${plot.name}</strong><span>10 × 20 quadrados • 200 novas parcelas • 1 novo espaço de torre</span></div>
      <div class="castle-expansion-payments">
        <article><h4>Madeira e pedra</h4><div class="costs">${ctx.costsHtml(prices.resources)}</div><button class="action-btn" data-action="castle-expansion-buy" data-expansion-id="${plot.id}" data-payment="resources">Comprar agora</button></article>
        <b>OU</b>
        <article><h4>Coroas</h4><div class="costs">${ctx.costsHtml(prices.crowns)}</div><button class="action-btn" data-action="castle-expansion-buy" data-expansion-id="${plot.id}" data-payment="crowns">Comprar agora</button></article>
      </div>
      <p class="castle-expansion-instant">Sem tempo de construção: a área e a torre ficam disponíveis imediatamente.</p>
    </div>`;
  }

  globalThis.ReinosCastleExpansions = Object.freeze({
    VERSION,
    GRID_SIZE,
    MAX_EXPANSIONS,
    INITIAL_REGION,
    PLOTS,
    normalizeState,
    regions,
    plotById,
    purchasedIds,
    availablePlots,
    isAvailable,
    isCellUnlocked,
    isRectUnlocked,
    cost,
    purchase,
    unlockedArea,
    bounds,
    cells,
    boundaryEdges,
    gateEdge,
    towerPoints,
    groundHtml,
    modalHtml,
    purchaseHtml
  });
})();
