import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
import L from 'leaflet'
import 'leaflet.heat'
import { apiUrl, downloadBlob, getAuthToken } from '../api'

const geojsonCacheByUrl = new Map()
const geojsonPromiseByUrl = new Map()

function escapeHtml(value) {
  return String(value ?? '')
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
}

function buildTooltipHtml(tooltip) {
  const title = String(tooltip?.title || '').trim()
  const label = String(tooltip?.label || '').trim()
  const value = String(tooltip?.value || '').trim()
  return [
    '<div style="font-family:\'Segoe UI\',Arial,sans-serif; font-size:14px; line-height:1.7; padding:2px 4px;">',
    title ? `<b>${escapeHtml(title)}</b>` : '',
    label && value ? `<br><span style="color:#555;">${escapeHtml(label)}:</span> <b>${escapeHtml(value)}</b>` : '',
    '</div>',
  ].join('')
}

function buildPopupErrorHtml(message) {
  const text = escapeHtml(message || 'Falha ao carregar os dados do registro.')
  return (
    '<div style="font-family:\'Segoe UI\'; border-radius:8px; overflow:hidden; width:340px; max-width:340px;">'
    + '<div style="background:#6c757d; color:white; padding:10px 15px; font-weight:600;">Dados do Registro</div>'
    + `<div style="padding:12px 15px; background:#f8f9fa; color:#b42318; font-size:12px;">${text}</div>`
    + '</div>'
  )
}

function normalizeCssColor(value, fallback = '#607d8b') {
  const text = String(value || '').trim()
  if (/^#[0-9a-fA-F]{3,8}$/.test(text)) return text
  if (/^rgba?\(\s*[\d.\s%,]+\)$/.test(text)) return text
  return fallback
}

function isTrueFlag(value) {
  return value === true || value === 1 || value === '1'
}

function isFalseFlag(value) {
  return value === false || value === 0 || value === '0'
}

function naturalCompare(a, b) {
  return String(a ?? '').localeCompare(String(b ?? ''), 'pt-BR', { numeric: true, sensitivity: 'base' })
}

function buildGroupedMarketMarkerHtml(item, size = 22) {
  const count = Math.max(2, Number(item?.count) || 2)
  const color = normalizeCssColor(item?.color || item?.fill_color || '#607d8b')
  const markerSize = Math.max(14, Math.min(38, Number(size) || 22))
  const fontSize = Math.max(9, Math.min(12, markerSize * 0.38))
  const borderSize = markerSize >= 24 ? 3 : 2
  const haloSize = markerSize >= 24 ? 4 : 2
  const labelHtml = isTrueFlag(item?.hide_count)
    ? ''
    : `<span>${escapeHtml(count > 99 ? '99+' : count)}</span>`
  return (
    `<div class="mesa-market-group-marker" style="--mesa-group-color:${color};--mesa-group-size:${markerSize}px;--mesa-group-font:${fontSize}px;--mesa-group-border:${borderSize}px;--mesa-group-halo:${haloSize}px;">`
    + labelHtml
    + '</div>'
  )
}

function buildGroupedMarketIcon(item, size = 22) {
  const markerSize = Math.max(14, Math.min(38, Number(size) || 22))
  return L.divIcon({
    html: buildGroupedMarketMarkerHtml(item, markerSize),
    iconSize: [markerSize, markerSize],
    iconAnchor: [markerSize / 2, markerSize / 2],
    className: 'mesa-market-group-icon',
  })
}

function hasValidRowId(value) {
  if (value === null || value === undefined || value === '') return false
  return Number.isInteger(Number(value)) && Number(value) >= 0
}

function parseFiniteCoordinate(value, min, max) {
  if (value === null || value === undefined) return null
  const text = String(value).trim().replace(',', '.')
  if (!text) return null
  const parsed = Number(text)
  if (!Number.isFinite(parsed)) return null
  if (parsed < min || parsed > max) return null
  return parsed
}

function parseLatLonPair(rawLat, rawLon) {
  const lat = parseFiniteCoordinate(rawLat, -90, 90)
  const lon = parseFiniteCoordinate(rawLon, -180, 180)
  if (lat === null || lon === null) return null
  return [lat, lon]
}

function ensurePopupPager(root) {
  if (!root || root.dataset.mesaPagerBound === '1') return
  root.dataset.mesaPagerBound = '1'

  const pages = Array.from(root.querySelectorAll('.mesa-popup-page'))
  if (!pages.length) return

  const numberWrap = root.querySelector('[data-page-number-wrap]')
  if (numberWrap) {
    numberWrap.innerHTML = ''
    pages.forEach((_, index) => {
      const button = document.createElement('button')
      button.type = 'button'
      button.dataset.pageNumber = String(index + 1)
      button.textContent = String(index + 1)
      button.style.border = '1px solid #ced8e2'
      button.style.background = '#fff'
      button.style.borderRadius = '6px'
      button.style.padding = '2px 7px'
      button.style.fontSize = '11px'
      button.style.cursor = 'pointer'
      button.style.color = '#4e6479'
      button.style.display = 'inline-flex'
      button.style.alignItems = 'center'
      button.style.justifyContent = 'center'
      numberWrap.appendChild(button)
    })
  }

  function updatePager(targetPage) {
    const total = pages.length
    let current = Number.parseInt(String(targetPage || root.dataset.currentPage || '1'), 10)
    if (!Number.isFinite(current) || current < 1) current = 1
    if (current > total) current = total
    root.dataset.currentPage = String(current)

    pages.forEach((pageEl, index) => {
      pageEl.style.display = index + 1 === current ? 'block' : 'none'
    })

    Array.from(root.querySelectorAll('[data-page-number]')).forEach((button) => {
      const value = Number.parseInt(String(button.dataset.pageNumber || '1'), 10)
      const ativo = value === current
      button.style.background = ativo ? '#eaf1f7' : '#fff'
      button.style.borderColor = ativo ? '#9fb4c8' : '#ced8e2'
      button.style.color = ativo ? '#2f4b66' : '#4e6479'
    })

    const onFirst = current <= 1
    const onLast = current >= total
    const navState = [
      ['first', onFirst],
      ['prev', onFirst],
      ['next', onLast],
      ['last', onLast],
    ]
    navState.forEach(([action, disabled]) => {
      const button = root.querySelector(`[data-a="${action}"]`)
      if (!button) return
      button.disabled = disabled
      button.style.opacity = disabled ? '0.45' : '1'
      button.style.cursor = disabled ? 'not-allowed' : 'pointer'
    })
  }

  root.addEventListener('click', (event) => {
    const button = event.target.closest('[data-a], [data-page-number]')
    if (!button) return
    event.preventDefault()
    event.stopPropagation()

    const current = Number.parseInt(String(root.dataset.currentPage || '1'), 10) || 1
    if (button.dataset.pageNumber) {
      updatePager(Number.parseInt(String(button.dataset.pageNumber), 10) || current)
      return
    }

    const action = String(button.dataset.a || '').trim()
    if (action === 'first') updatePager(1)
    if (action === 'prev') updatePager(current - 1)
    if (action === 'next') updatePager(current + 1)
    if (action === 'last') updatePager(pages.length)
  })

  updatePager(1)
}

async function carregarGeojson(url) {
  const endpoint = String(url || '').trim()
  if (!endpoint) return null
  if (geojsonCacheByUrl.has(endpoint)) return geojsonCacheByUrl.get(endpoint)
  if (geojsonPromiseByUrl.has(endpoint)) return geojsonPromiseByUrl.get(endpoint)

  // Em desenvolvimento, o mapa é servido pelo Vite e a API pelo backend.
  // Usar `apiUrl` evita que /api/... seja solicitado à porta do frontend.
  const requestUrl = /^https?:\/\//i.test(endpoint) ? endpoint : apiUrl(endpoint)

  const promise = fetch(requestUrl, {
    headers: (() => {
      const token = getAuthToken()
      return token ? { 'X-Auth-Token': token } : {}
    })(),
  })
    .then(async (response) => {
      if (!response.ok) {
        throw new Error('Falha ao carregar camada vetorial.')
      }
      return response.json()
    })
    .then((payload) => {
      geojsonCacheByUrl.set(endpoint, payload)
      return payload
    })
    .finally(() => {
      geojsonPromiseByUrl.delete(endpoint)
    })
  geojsonPromiseByUrl.set(endpoint, promise)
  return promise
}

function construirLegendaControl(legend) {
  if (!legend) return null

  return L.control({ position: 'bottomright' })
}

function formatLegendNumber(value) {
  const number = Number(value)
  if (!Number.isFinite(number)) return ''
  return number.toLocaleString('pt-BR', { maximumFractionDigits: 2 })
}

function buildLegendHtml(legend) {
  const title = escapeHtml(String(legend?.title || '').trim())
  const colors = Array.isArray(legend?.colors) ? legend.colors.filter(Boolean) : []
  const gradient = colors.length ? colors.join(', ') : '#2ecc71, #e74c3c'
  const tickValues = Array.isArray(legend?.tick_values) ? legend.tick_values : []
  const tickLabels = Array.isArray(legend?.tick_labels) ? legend.tick_labels : []
  const hasTicks = tickValues.length > 1 && tickValues.length === tickLabels.length

  const ticksHtml = hasTicks
    ? (
        '<div class="mesa-leaflet-legend-ticks">'
        + tickValues.map((tickValue, index) => {
          const rawMin = Number(legend?.vmin)
          const rawMax = Number(legend?.vmax)
          const ratio = Number.isFinite(rawMin) && Number.isFinite(rawMax) && rawMax > rawMin
            ? (Number(tickValue) - rawMin) / (rawMax - rawMin)
            : index / Math.max(1, tickValues.length - 1)
          const left = Math.max(0, Math.min(100, ratio * 100))
          return (
            `<span class="mesa-leaflet-legend-tick" style="left:${left.toFixed(4)}%;">`
            + `${escapeHtml(String(tickLabels[index] || ''))}`
            + '</span>'
          )
        }).join('')
        + '</div>'
      )
    : ''

  const scaleHtml = hasTicks
    ? ''
    : (
        '<div class="mesa-leaflet-legend-scale">'
        + `<span>${escapeHtml(formatLegendNumber(legend?.vmin))}</span>`
        + `<span>${escapeHtml(formatLegendNumber(legend?.vmax))}</span>`
        + '</div>'
      )

  return [
    title ? `<strong>${title}</strong>` : '',
    `<div class="mesa-leaflet-legend-bar" style="background: linear-gradient(90deg, ${gradient});"></div>`,
    ticksHtml,
    scaleHtml,
  ].join('')
}

function addNoticeControl(map, notice) {
  const message = String(notice?.message || '').trim()
  if (!message) return null
  const control = L.control({ position: notice?.position || 'topright' })
  control.onAdd = () => {
    const div = L.DomUtil.create('div', 'mesa-leaflet-notice')
    div.innerHTML = escapeHtml(message)
    return div
  }
  control.addTo(map)
  return control
}

function buildIndiceBadgeHtml(indice) {
  return (
    '<div style="transform: translate(10px, -14px);display:inline-block;background: rgba(255, 255, 255, 0.9);'
    + 'border: 1px solid rgba(28, 45, 66, 0.45);border-radius: 10px;padding: 1px 6px;font-size: 11px;'
    + 'font-weight: 700;line-height: 1.2;color: #1f2f44;white-space: nowrap;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);'
    + 'pointer-events: none;">'
    + `${escapeHtml(indice)}`
    + '</div>'
  )
}

function formatCoordinate(value, positiveLabel, negativeLabel) {
  const numeric = Number(value)
  if (!Number.isFinite(numeric)) return '-'
  const absolute = Math.abs(numeric)
  const degrees = Math.floor(absolute)
  const minutesFloat = (absolute - degrees) * 60
  const minutes = Math.floor(minutesFloat)
  const seconds = ((minutesFloat - minutes) * 60).toFixed(2)
  const direction = numeric >= 0 ? positiveLabel : negativeLabel
  return `${degrees}° ${minutes.toString().padStart(2, '0')}' ${seconds.padStart(5, '0')}" ${direction}`
}

function formatLatLng(latlng) {
  if (!latlng) return ''
  return `${formatCoordinate(latlng.lat, 'N', 'S')} / ${formatCoordinate(latlng.lng, 'L', 'W')}`
}

function formatDecimalLatLng(latlng) {
  if (!latlng) return ''
  return `${Number(latlng.lat).toFixed(6)} / ${Number(latlng.lng).toFixed(6)}`
}

function formatDistance(distanceMeters) {
  const value = Number(distanceMeters)
  if (!Number.isFinite(value) || value <= 0) return '0 m'
  if (value >= 1000) {
    return `${(value / 1000).toLocaleString('pt-BR', { maximumFractionDigits: 2 })} km`
  }
  return `${value.toLocaleString('pt-BR', { maximumFractionDigits: 1 })} m`
}

function pointInPolygon(point, polygon) {
  if (!point || !Array.isArray(polygon) || polygon.length < 3) return false
  let inside = false
  for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i, i += 1) {
    const xi = polygon[i].x
    const yi = polygon[i].y
    const xj = polygon[j].x
    const yj = polygon[j].y
    const intersects = ((yi > point.y) !== (yj > point.y))
      && (point.x < ((xj - xi) * (point.y - yi)) / ((yj - yi) || Number.EPSILON) + xi)
    if (intersects) inside = !inside
  }
  return inside
}

function computePolygonArea(latlngs) {
  if (!Array.isArray(latlngs) || latlngs.length < 3) return 0
  const d2r = Math.PI / 180
  const radius = 6378137
  let area = 0
  for (let index = 0; index < latlngs.length; index += 1) {
    const current = latlngs[index]
    const next = latlngs[(index + 1) % latlngs.length]
    area += (
      (Number(next.lng) - Number(current.lng)) * d2r
      * (2 + Math.sin(Number(current.lat) * d2r) + Math.sin(Number(next.lat) * d2r))
    )
  }
  return Math.abs(area * radius * radius / 2)
}

function formatArea(areaSquareMeters) {
  const value = Number(areaSquareMeters)
  if (!Number.isFinite(value) || value <= 0) return ''
  if (value >= 10000) {
    return `${(value / 10000).toLocaleString('pt-BR', { maximumFractionDigits: 2 })} ha`
  }
  return `${value.toLocaleString('pt-BR', { maximumFractionDigits: 0 })} m²`
}

function emitOpenTrabalhoTecnicoMessage(trabalhoId, origem = 'pesquisa_mapa') {
  const id = String(trabalhoId || '').trim()
  if (!id) return false
  const payload = {
    type: 'mesa:open-trabalho-tecnico',
    trabalhoId: id,
    origem: String(origem || 'pesquisa_mapa').trim() || 'pesquisa_mapa',
  }

  try {
    window.postMessage(payload, '*')
    return true
  } catch {
    return false
  }
}

function buildLegacyOverlayLayers(payload) {
  const overlays = []

  if (payload?.show_bairros) {
    overlays.push({
      id: 'bairros',
      label: 'Bairros',
      show: true,
      geojson_url: payload?.bairros_geojson_url || '',
      geojson_style: {
        color: '#4c6882',
        weight: 1.15,
        opacity: 0.88,
        fillColor: '#f39c12',
        fillOpacity: 0,
      },
      geojson_tooltip_properties: ['NOME', 'BAIRRO', 'NME_BAI', 'NOME_BAIRRO'],
      geojson_tooltip_label: 'Bairro',
    })
  }

  // Alguns mapas antigos ainda usam o payload legado. Mantemos a camada aqui
  // para que a disponibilidade da inundação seja igual à dos mapas novos.
  if (payload?.show_inundacao !== false) {
    overlays.push({
      id: 'inundacao_535',
      label: 'Inundação — cota 5,35',
      show: false,
      lazy: true,
      geojson_url: payload?.inundacao_geojson_url || '/api/visualizacao/map/inundacao.geojson',
      geojson_pane: 'mesa-inundacao-pane',
      geojson_style: {
        color: '#1677c8',
        weight: 1.2,
        opacity: 0.9,
        fillColor: '#3ca6e8',
        fillOpacity: 0.2,
      },
      geojson_tooltip_properties: ['contourmax'],
      geojson_tooltip_label: 'Cota',
    })
  }

  if (Array.isArray(payload?.market_points) && payload.market_points.length) {
    overlays.push({
      id: 'mercado',
      label: 'Mercado',
      show: true,
      points: payload.market_points.map((item) => ({
        ...item,
        popup_request: !item?.grouped && hasValidRowId(item?.row_id)
          ? { kind: 'visualizacao_row', row_id: Number(item.row_id) }
          : null,
      })),
    })

    overlays.push({
      id: 'indices',
      label: 'INDICES',
      show: Boolean(payload?.show_indices),
      markers: payload.market_points.map((item) => ({
        lat: Number(item?.lat),
        lon: Number(item?.lon),
        marker_html: buildIndiceBadgeHtml(item?.indice),
        icon_size: [72, 24],
        icon_anchor: [0, 0],
        class_name: 'mesa-indice-label',
        interactive: false,
        keyboard: false,
      })),
    })
  }

  if (Array.isArray(payload?.trabalhos_tecnicos_points) && payload.trabalhos_tecnicos_points.length) {
    overlays.push({
      id: 'trabalhos-tecnicos',
      label: 'Trabalhos técnicos',
      show: payload?.trabalhos_tecnicos_show !== false,
      markers: payload.trabalhos_tecnicos_points,
    })
  }

  return overlays
}

async function readJsonSafely(response) {
  const raw = await response.text()
  if (!raw || !raw.trim()) return null
  try {
    return JSON.parse(raw)
  } catch {
    return null
  }
}

function waitForNextPaint() {
  return new Promise((resolve) => {
    window.requestAnimationFrame(() => {
      window.requestAnimationFrame(resolve)
    })
  })
}

function isDrawableVisible(rect, hostRect) {
  if (!rect || !hostRect) return false
  if (rect.width <= 0 || rect.height <= 0) return false
  return (
    rect.right > hostRect.left
    && rect.left < hostRect.right
    && rect.bottom > hostRect.top
    && rect.top < hostRect.bottom
  )
}

function parseBorderRadiusPx(element) {
  if (!element) return 0
  const computed = window.getComputedStyle(element)
  const raw = String(computed.borderTopLeftRadius || computed.borderRadius || '0')
  const parsed = Number.parseFloat(raw)
  return Number.isFinite(parsed) ? Math.max(0, parsed) : 0
}

function clipRoundedRect(ctx, width, height, radius) {
  const corner = Math.max(0, Math.min(radius, Math.min(width, height) / 2))
  if (!corner) {
    ctx.beginPath()
    ctx.rect(0, 0, width, height)
    ctx.clip()
    return
  }
  ctx.beginPath()
  ctx.moveTo(corner, 0)
  ctx.lineTo(width - corner, 0)
  ctx.quadraticCurveTo(width, 0, width, corner)
  ctx.lineTo(width, height - corner)
  ctx.quadraticCurveTo(width, height, width - corner, height)
  ctx.lineTo(corner, height)
  ctx.quadraticCurveTo(0, height, 0, height - corner)
  ctx.lineTo(0, corner)
  ctx.quadraticCurveTo(0, 0, corner, 0)
  ctx.closePath()
  ctx.clip()
}

function collectVisibleMapDrawables(container, hostRect) {
  const drawables = []
  let sequence = 0
  const register = (element, kind) => {
    if (!element) return
    const rect = element.getBoundingClientRect()
    if (!isDrawableVisible(rect, hostRect)) return
    const pane = element.closest('.leaflet-pane')
    const paneZ = Number.parseInt(String(window.getComputedStyle(pane || element).zIndex || '0'), 10)
    drawables.push({
      element,
      kind,
      rect,
      paneZ: Number.isFinite(paneZ) ? paneZ : 0,
      sequence,
    })
    sequence += 1
  }

  Array.from(container.querySelectorAll('.leaflet-tile-pane img.leaflet-tile')).forEach((element) => register(element, 'image'))
  Array.from(container.querySelectorAll('.leaflet-pane canvas')).forEach((element) => register(element, 'canvas'))
  Array.from(container.querySelectorAll('.leaflet-pane svg')).forEach((element) => register(element, 'svg'))

  drawables.sort((left, right) => {
    if (left.paneZ !== right.paneZ) return left.paneZ - right.paneZ
    return left.sequence - right.sequence
  })
  return drawables
}

function serializeSvgElement(element, width, height) {
  const clone = element.cloneNode(true)
  clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
  clone.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink')
  clone.setAttribute('width', String(width))
  clone.setAttribute('height', String(height))
  if (!clone.getAttribute('viewBox')) {
    clone.setAttribute('viewBox', `0 0 ${width} ${height}`)
  }
  return new XMLSerializer().serializeToString(clone)
}

function loadSvgImage(svgMarkup) {
  return new Promise((resolve, reject) => {
    const blob = new Blob([svgMarkup], { type: 'image/svg+xml;charset=utf-8' })
    const objectUrl = URL.createObjectURL(blob)
    const image = new Image()
    image.decoding = 'async'
    image.onload = () => {
      URL.revokeObjectURL(objectUrl)
      resolve(image)
    }
    image.onerror = () => {
      URL.revokeObjectURL(objectUrl)
      reject(new Error('Nao foi possivel desenhar uma camada vetorial do mapa.'))
    }
    image.src = objectUrl
  })
}

function canvasToBlob(canvas) {
  return new Promise((resolve, reject) => {
    try {
      canvas.toBlob((blob) => {
        if (blob) {
          resolve(blob)
          return
        }
        reject(new Error('Nao foi possivel gerar a imagem PNG do mapa.'))
      }, 'image/png')
    } catch (error) {
      reject(error)
    }
  })
}

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

function normalizeFitPadding(value, fallback = 48) {
  const numeric = Number(value)
  const resolved = Number.isFinite(numeric) && numeric >= 0 ? numeric : fallback
  return [resolved, resolved]
}

function normalizeSelectionRect(selectionRect, containerWidth, containerHeight) {
  const rawLeft = Number(selectionRect?.left ?? selectionRect?.x)
  const rawTop = Number(selectionRect?.top ?? selectionRect?.y)
  const rawWidth = Number(selectionRect?.width)
  const rawHeight = Number(selectionRect?.height)
  if (![rawLeft, rawTop, rawWidth, rawHeight].every(Number.isFinite)) return null
  if (rawWidth <= 0 || rawHeight <= 0) return null

  const left = clampNumber(rawLeft, 0, containerWidth)
  const top = clampNumber(rawTop, 0, containerHeight)
  const right = clampNumber(rawLeft + rawWidth, 0, containerWidth)
  const bottom = clampNumber(rawTop + rawHeight, 0, containerHeight)
  const width = right - left
  const height = bottom - top
  if (width < 12 || height < 12) return null
  return {
    left,
    top,
    width,
    height,
    right,
    bottom,
  }
}

function intersectRectangles(leftRect, rightRect) {
  const left = Math.max(leftRect.left, rightRect.left)
  const top = Math.max(leftRect.top, rightRect.top)
  const right = Math.min(leftRect.right, rightRect.right)
  const bottom = Math.min(leftRect.bottom, rightRect.bottom)
  if (right <= left || bottom <= top) return null
  return {
    left,
    top,
    right,
    bottom,
    width: right - left,
    height: bottom - top,
  }
}

function resolveDrawableSourceSize(element, kind, drawWidth, drawHeight) {
  if (kind === 'image') {
    return {
      width: Number(element?.naturalWidth) || drawWidth,
      height: Number(element?.naturalHeight) || drawHeight,
    }
  }
  if (kind === 'canvas') {
    return {
      width: Number(element?.width) || drawWidth,
      height: Number(element?.height) || drawHeight,
    }
  }
  return { width: drawWidth, height: drawHeight }
}

function computeSelectionExportScale(selectionRect) {
  const longestEdge = Math.max(selectionRect?.width || 0, selectionRect?.height || 0, 1)
  const deviceScale = Number(window.devicePixelRatio) || 1
  const preferredScale = Math.max(3, deviceScale * 2.5)
  const boundedScale = 3600 / longestEdge
  return Math.max(2, Math.min(5, preferredScale, boundedScale))
}

function fitMapToPayloadBounds(map, payload, padding = 48) {
  if (!map) return false
  const bounds = Array.isArray(payload?.bounds) ? payload.bounds : null
  if (bounds && bounds.length === 2) {
    map.fitBounds(bounds, { padding: normalizeFitPadding(padding), maxZoom: 18, animate: false })
    return true
  }
  if (Array.isArray(payload?.center) && payload.center.length === 2) {
    map.setView(payload.center, 12, { animate: false })
    return true
  }
  return false
}

function addFullscreenControl(map, onToggleFullscreen) {
  const control = L.control({ position: 'topright' })
  let root = null

  control.onAdd = () => {
    root = L.DomUtil.create('div', 'mesa-leaflet-fullscreen leaflet-bar leaflet-control')
    const button = L.DomUtil.create('a', 'mesa-leaflet-fullscreen-toggle', root)
    button.href = '#'
    button.title = 'Abrir mapa em tela cheia'
    button.setAttribute('aria-label', 'Abrir mapa em tela cheia')
    button.innerHTML = '<span class="mesa-sr-only">Abrir mapa em tela cheia</span>'

    const updateState = () => {
      const active = Boolean(document.fullscreenElement)
      root?.classList.toggle('is-active', active)
      button.title = active ? 'Sair da tela cheia' : 'Abrir mapa em tela cheia'
      button.setAttribute('aria-label', active ? 'Sair da tela cheia' : 'Abrir mapa em tela cheia')
    }

    const fallbackToggle = async () => {
      if (document.fullscreenElement) {
        await document.exitFullscreen()
        return
      }
      const container = map.getContainer()
      if (typeof container?.requestFullscreen === 'function') {
        await container.requestFullscreen()
      }
    }

    L.DomEvent.disableClickPropagation(root)
    L.DomEvent.disableScrollPropagation(root)
    L.DomEvent.on(button, 'click', (event) => {
      L.DomEvent.stop(event)
      const result = onToggleFullscreen ? onToggleFullscreen(event) : fallbackToggle()
      Promise.resolve(result).catch(() => {}).finally(() => {
        window.setTimeout(() => {
          map.invalidateSize?.(true)
        }, 120)
      })
    })
    document.addEventListener('fullscreenchange', updateState)
    root._mesaFullscreenCleanup = () => {
      document.removeEventListener('fullscreenchange', updateState)
    }
    updateState()
    return root
  }

  control.onRemove = () => {
    root?._mesaFullscreenCleanup?.()
    root = null
  }

  control.addTo(map)
  return control
}

async function waitForTileLayerLoad(tileLayer, timeoutMs = 6500) {
  if (!tileLayer) return
  await new Promise((resolve) => {
    let settled = false
    const finalize = () => {
      if (settled) return
      settled = true
      window.clearTimeout(timerId)
      tileLayer.off('load', finalize)
      resolve()
    }
    const timerId = window.setTimeout(finalize, timeoutMs)
    tileLayer.on('load', finalize)
    if (!tileLayer._loading) {
      window.setTimeout(finalize, 180)
    }
  })
}

function resolveActiveTileLayerConfig(sourceMap, payload) {
  const definitions = Array.isArray(payload?.tile_layers) ? payload.tile_layers : []
  if (!sourceMap || !definitions.length) return definitions[0] || null

  const activeUrls = new Set()
  sourceMap.eachLayer((layer) => {
    if (layer instanceof L.TileLayer && sourceMap.hasLayer(layer)) {
      const url = String(layer?._url || '').trim()
      if (url) activeUrls.add(url)
    }
  })
  return definitions.find((item) => activeUrls.has(String(item?.url || '').trim())) || definitions[0] || null
}

async function buildHighResolutionSelectionBlob({
  sourceMap,
  sourceContainer,
  payload,
  selectionRect,
}) {
  if (!sourceMap || !sourceContainer) {
    throw new Error('Mapa indisponivel para exportacao.')
  }

  const hostRect = sourceContainer.getBoundingClientRect()
  const containerWidth = Math.round(hostRect.width)
  const containerHeight = Math.round(hostRect.height)
  const normalizedSelection = normalizeSelectionRect(selectionRect, containerWidth, containerHeight)
  if (!normalizedSelection) {
    throw new Error('Selecione um retangulo valido no mapa antes de baixar.')
  }

  const exportScale = computeSelectionExportScale(normalizedSelection)
  const exportWidth = Math.max(480, Math.round(normalizedSelection.width * exportScale))
  const exportHeight = Math.max(480, Math.round(normalizedSelection.height * exportScale))
  const selectionCenter = sourceMap.containerPointToLatLng([
    normalizedSelection.left + (normalizedSelection.width / 2),
    normalizedSelection.top + (normalizedSelection.height / 2),
  ])
  const sourceZoom = Number(sourceMap.getZoom?.() ?? 0)
  const exportZoom = sourceZoom + Math.log2(exportScale)

  const mount = document.createElement('div')
  mount.style.position = 'fixed'
  mount.style.left = '-20000px'
  mount.style.top = '0'
  mount.style.width = `${exportWidth}px`
  mount.style.height = `${exportHeight}px`
  mount.style.opacity = '0'
  mount.style.pointerEvents = 'none'
  mount.style.background = '#ffffff'
  mount.style.overflow = 'hidden'
  document.body.appendChild(mount)

  let exportMap = null
  try {
    exportMap = L.map(mount, {
      zoomControl: false,
      attributionControl: false,
      preferCanvas: true,
      dragging: false,
      scrollWheelZoom: false,
      doubleClickZoom: false,
      boxZoom: false,
      keyboard: false,
      touchZoom: false,
      tapHold: false,
    })

    const bairrosPane = exportMap.createPane('mesa-bairros-pane')
    const marketPane = exportMap.createPane('mesa-market-pane')
    const marketGroupPane = exportMap.createPane('mesa-market-group-pane')
    const trabalhosPane = exportMap.createPane('mesa-trabalhos-pane')
    const indicesPane = exportMap.createPane('mesa-indices-pane')
    bairrosPane.style.zIndex = '410'
    marketPane.style.zIndex = '420'
    marketGroupPane.style.zIndex = '425'
    trabalhosPane.style.zIndex = '430'
    indicesPane.style.zIndex = '440'

    const canvasRenderer = L.canvas({ padding: 0.5, pane: 'mesa-market-pane' })
    const overlaySpecs = Array.isArray(payload?.overlay_layers) && payload.overlay_layers.length
      ? payload.overlay_layers
      : buildLegacyOverlayLayers(payload)
    const responsivePointContainers = []
    const radiusBehavior = payload?.radius_behavior || {}
    const minRadius = Number(radiusBehavior.min_radius) || 1.6
    const maxRadius = Number(radiusBehavior.max_radius) || 52.0
    const referenceZoom = Number(radiusBehavior.reference_zoom) || 12.0
    const growthFactor = Number(radiusBehavior.growth_factor) || 0.2

    const activeTileLayerConfig = resolveActiveTileLayerConfig(sourceMap, payload)
    let tileLayer = null
    if (activeTileLayerConfig?.url) {
      tileLayer = L.tileLayer(String(activeTileLayerConfig.url || ''), {
        attribution: String(activeTileLayerConfig?.attribution || ''),
        crossOrigin: 'anonymous',
        detectRetina: true,
      }).addTo(exportMap)
    }

    const applyResponsiveRadius = () => {
      const zoom = typeof exportMap.getZoom === 'function' ? exportMap.getZoom() : referenceZoom
      const zoomDelta = zoom - referenceZoom
      const expFactor = 2 ** (zoomDelta * growthFactor)
      let floorScale = 1.0
      if (zoomDelta >= 0) {
        floorScale = 1 + zoomDelta * 0.22
        if (zoom >= 15) {
          floorScale += (zoom - 14) * 0.30
        }
      } else {
        floorScale = Math.max(0.28, 1 + zoomDelta * 0.20)
      }

      responsivePointContainers.forEach((container) => {
        if (!container || typeof container.eachLayer !== 'function') return
        container.eachLayer((layer) => {
          if (typeof layer.setRadius !== 'function') return
          const base = Number(layer.options?.mesaBaseRadius || layer.options?.radius || 4)
          const dynamicMin = Math.max(minRadius, base * floorScale)
          const dynamicMax = Math.max(dynamicMin + 0.1, Math.min(maxRadius, base * 8.0))
          layer.setRadius(clampNumber(base * expFactor, dynamicMin, dynamicMax))
        })
      })
    }

    const addPointMarkers = (layerGroup, items) => {
      if (!Array.isArray(items) || !items.length) return
      responsivePointContainers.push(layerGroup)
      items.forEach((item) => {
        const latlng = parseLatLonPair(item?.lat, item?.lon)
        if (!latlng) return
        if (item?.grouped) {
          const marker = L.marker(latlng, {
            icon: buildGroupedMarketIcon(item, 16),
            pane: String(item?.pane || 'mesa-market-group-pane'),
            interactive: false,
            keyboard: false,
            bubblingMouseEvents: false,
          })
          marker.options.mesaBaseRadius = Number(item?.base_radius) || 4
          marker.options.mesaGroupedMarketMarker = true
          marker.options.mesaGroupedMarketItem = item
          layerGroup.addLayer(marker)
          return
        }
        const marker = L.circleMarker(latlng, {
          renderer: canvasRenderer,
          pane: String(item?.pane || 'mesa-market-pane'),
          radius: Number(item?.base_radius) || 4,
          color: String(item?.stroke_color || '#000000'),
          weight: Number.isFinite(Number(item?.stroke_weight))
            ? Number(item.stroke_weight)
            : (Number(item?.base_radius) > 4 ? 1 : 0.8),
          fill: item?.fill !== false,
          fillColor: String(item?.color || item?.fill_color || '#FF8C00'),
          fillOpacity: Number.isFinite(Number(item?.fill_opacity)) ? Number(item.fill_opacity) : 0.68,
          interactive: false,
          bubblingMouseEvents: false,
        })
        marker.options.mesaBaseRadius = Number(item?.base_radius) || 4
        layerGroup.addLayer(marker)
      })
    }

    const addShapeOverlays = (layerGroup, shapes) => {
      if (!Array.isArray(shapes) || !shapes.length) return
      shapes.forEach((shape) => {
        const shapeType = String(shape?.type || shape?.shape_type || '').trim().toLowerCase()
        const style = {
          color: String(shape?.color || '#1f6fb2'),
          weight: Number.isFinite(Number(shape?.weight)) ? Number(shape.weight) : 2,
          opacity: Number.isFinite(Number(shape?.opacity)) ? Number(shape.opacity) : 0.8,
          fill: shape?.fill === true,
          fillColor: String(shape?.fill_color || shape?.color || '#1f6fb2'),
          fillOpacity: Number.isFinite(Number(shape?.fill_opacity)) ? Number(shape.fill_opacity) : 0.12,
          dashArray: shape?.dash_array ? String(shape.dash_array) : undefined,
          pane: String(shape?.pane || 'mesa-bairros-pane'),
          interactive: false,
        }
        let layer = null

        if (shapeType === 'circle' && Array.isArray(shape?.center) && shape.center.length === 2) {
          const center = parseLatLonPair(shape.center[0], shape.center[1])
          if (center) {
            layer = L.circle(center, { ...style, radius: Number(shape?.radius_m) || 0 })
          }
        } else if (shapeType === 'polyline' && Array.isArray(shape?.coords) && shape.coords.length) {
          layer = L.polyline(shape.coords, style)
        } else if (shapeType === 'polygon' && Array.isArray(shape?.coords) && shape.coords.length) {
          layer = L.polygon(shape.coords, style)
        } else if (shapeType === 'circlemarker' && Array.isArray(shape?.center) && shape.center.length === 2) {
          const center = parseLatLonPair(shape.center[0], shape.center[1])
          if (center) {
            layer = L.circleMarker(center, { ...style, radius: Number(shape?.radius) || 6 })
          }
        }

        if (layer) {
          layerGroup.addLayer(layer)
        }
      })
    }

    const addGeoJsonOverlay = async (layerGroup, spec) => {
      const geojsonLayer = L.geoJSON(null, {
        pane: String(spec?.geojson_pane || 'mesa-bairros-pane'),
        style: () => ({
          color: String(spec?.geojson_style?.color || '#4c6882'),
          weight: Number.isFinite(Number(spec?.geojson_style?.weight)) ? Number(spec.geojson_style.weight) : 1.0,
          opacity: Number.isFinite(Number(spec?.geojson_style?.opacity))
            ? Number(spec.geojson_style.opacity)
            : 0.88,
          fillColor: String(spec?.geojson_style?.fillColor || '#f39c12'),
          fillOpacity: Number.isFinite(Number(spec?.geojson_style?.fillOpacity))
            ? Number(spec.geojson_style.fillOpacity)
            : 0.04,
          interactive: false,
        }),
      })
      layerGroup.addLayer(geojsonLayer)

      if (spec?.geojson_data) {
        geojsonLayer.addData(spec.geojson_data)
        return
      }
      if (!spec?.geojson_url) return
      try {
        const geojson = await carregarGeojson(spec.geojson_url)
        if (geojson) {
          geojsonLayer.addData(geojson)
        }
      } catch {
        // Camada opcional; manter o download funcional mesmo sem bairros.
      }
    }

    const addHeatmapOverlay = (layerGroup, heatmapSpec) => {
      if (!heatmapSpec || typeof L.heatLayer !== 'function') return
      const points = Array.isArray(heatmapSpec?.points)
        ? heatmapSpec.points
          .map((item) => {
            const latlng = parseLatLonPair(item?.lat, item?.lon)
            const weight = Number(item?.weight)
            if (!latlng) return null
            if (Number.isFinite(weight)) return [latlng[0], latlng[1], weight]
            return latlng
          })
          .filter(Boolean)
        : []
      if (!points.length) return
      const layer = L.heatLayer(points, {
        radius: Number(heatmapSpec?.radius) || 20,
        blur: Number(heatmapSpec?.blur) || 18,
        minOpacity: Number.isFinite(Number(heatmapSpec?.min_opacity)) ? Number(heatmapSpec.min_opacity) : 0.28,
        maxZoom: Number(heatmapSpec?.max_zoom) || 17,
        gradient: heatmapSpec?.gradient || undefined,
      })
      layerGroup.addLayer(layer)
    }

    const overlayPromises = overlaySpecs.map(async (spec) => {
      if (spec?.show === false) return
      const layerGroup = L.layerGroup().addTo(exportMap)
      if (spec?.geojson_url || spec?.geojson_data) {
        await addGeoJsonOverlay(layerGroup, spec)
      }
      addHeatmapOverlay(layerGroup, spec?.heatmap)
      addShapeOverlays(layerGroup, spec?.shapes)
      addPointMarkers(layerGroup, spec?.points)
    })

    exportMap.setView(selectionCenter, exportZoom, { animate: false })
    applyResponsiveRadius()
    exportMap.on('zoomend', applyResponsiveRadius)

    await Promise.all(overlayPromises)
    await waitForTileLayerLoad(tileLayer)
    await waitForNextPaint()
    await waitForNextPaint()

    const blob = await captureContainerRegionBlob(mount, null, 1)
    exportMap.off('zoomend', applyResponsiveRadius)
    return blob
  } finally {
    if (exportMap) {
      exportMap.remove()
    }
    mount.remove()
  }
}

async function captureContainerRegionBlob(container, selectionRect = null, scaleOverride = null) {
  const hostRect = container.getBoundingClientRect()
  const containerWidth = Math.round(hostRect.width)
  const containerHeight = Math.round(hostRect.height)
  const normalizedSelection = selectionRect
    ? normalizeSelectionRect(selectionRect, containerWidth, containerHeight)
    : {
        left: 0,
        top: 0,
        width: containerWidth,
        height: containerHeight,
        right: containerWidth,
        bottom: containerHeight,
      }

  if (!normalizedSelection || normalizedSelection.width <= 0 || normalizedSelection.height <= 0) {
    throw new Error('Nao foi possivel montar a area de exportacao do mapa.')
  }

  const scale = Number.isFinite(Number(scaleOverride)) && Number(scaleOverride) > 0
    ? Number(scaleOverride)
    : computeSelectionExportScale(normalizedSelection)
  const canvas = document.createElement('canvas')
  canvas.width = Math.max(1, Math.round(normalizedSelection.width * scale))
  canvas.height = Math.max(1, Math.round(normalizedSelection.height * scale))

  const ctx = canvas.getContext('2d')
  if (!ctx) {
    throw new Error('O navegador nao conseguiu montar o canvas de exportacao.')
  }

  ctx.scale(scale, scale)
  ctx.imageSmoothingEnabled = true
  ctx.imageSmoothingQuality = 'high'
  ctx.fillStyle = '#ffffff'
  ctx.fillRect(0, 0, normalizedSelection.width, normalizedSelection.height)

  const drawables = collectVisibleMapDrawables(container, hostRect)
  for (const drawable of drawables) {
    const { element, kind, rect } = drawable
    const drawableRect = {
      left: rect.left - hostRect.left,
      top: rect.top - hostRect.top,
      right: rect.right - hostRect.left,
      bottom: rect.bottom - hostRect.top,
      width: rect.width,
      height: rect.height,
    }
    const overlap = intersectRectangles(drawableRect, normalizedSelection)
    if (!overlap) continue

    const sourceWidth = Math.max(1, drawableRect.width)
    const sourceHeight = Math.max(1, drawableRect.height)
    const sourceSize = resolveDrawableSourceSize(element, kind, sourceWidth, sourceHeight)
    const ratioX = sourceSize.width / sourceWidth
    const ratioY = sourceSize.height / sourceHeight
    const sx = (overlap.left - drawableRect.left) * ratioX
    const sy = (overlap.top - drawableRect.top) * ratioY
    const sw = overlap.width * ratioX
    const sh = overlap.height * ratioY
    const dx = overlap.left - normalizedSelection.left
    const dy = overlap.top - normalizedSelection.top

    let source = element
    if (kind === 'svg') {
      const svgMarkup = serializeSvgElement(element, sourceWidth, sourceHeight)
      source = await loadSvgImage(svgMarkup)
    }

    ctx.drawImage(source, sx, sy, sw, sh, dx, dy, overlap.width, overlap.height)
  }

  return canvasToBlob(canvas)
}

const LeafletMapFrame = forwardRef(function LeafletMapFrame({ payload, sessionId, onToggleFullscreen }, ref) {
  const hostRef = useRef(null)
  const mapRef = useRef(null)
  const payloadRef = useRef(payload)
  const popupCacheRef = useRef(new Map())
  const popupLoaderRef = useRef(null)
  const groupSelectionMarkerRef = useRef(null)
  const [runtimeError, setRuntimeError] = useState('')
  const [groupSelection, setGroupSelection] = useState(null)

  payloadRef.current = payload

  useImperativeHandle(ref, () => ({
    fitToPayloadBounds(padding = 48) {
      const map = mapRef.current
      if (!map) return false
      return fitMapToPayloadBounds(map, payloadRef.current, padding)
    },
    async downloadSelectionPng(selectionRect, fileName = 'mapa-recorte.png') {
      const container = hostRef.current
      const blob = await buildHighResolutionSelectionBlob({
        sourceMap: mapRef.current,
        sourceContainer: container,
        payload: payloadRef.current,
        selectionRect,
      })
      downloadBlob(blob, fileName)
      return true
    },
    async downloadVisiblePng(fileName = 'mapa.png') {
      const container = hostRef.current
      if (!container) {
        throw new Error('Mapa indisponivel para exportacao.')
      }

      mapRef.current?.invalidateSize?.(false)
      await waitForNextPaint()

      const hostRect = container.getBoundingClientRect()
      const width = Math.round(hostRect.width)
      const height = Math.round(hostRect.height)
      if (width <= 0 || height <= 0) {
        throw new Error('O mapa ainda nao terminou de renderizar para exportacao.')
      }

      const deviceScale = Number(window.devicePixelRatio) || 1
      const scale = Math.max(3, Math.min(5, deviceScale * 2))
      const blob = await captureContainerRegionBlob(container, null, scale)
      downloadBlob(blob, fileName)
      return true
    },
  }), [])

  const closeGroupSelection = useCallback(() => {
    setGroupSelection(null)
    groupSelectionMarkerRef.current = null
  }, [])

  const selectGroupedMarketItem = useCallback((item) => {
    const popupRequest = item?.popup_request
    const marker = groupSelectionMarkerRef.current
    const loader = popupLoaderRef.current
    if (!popupRequest || !marker || !loader) return
    setGroupSelection(null)
    void loader(popupRequest, marker)
  }, [])

  useEffect(() => {
    if (!hostRef.current || !payload) return undefined
    let disposed = false
    setRuntimeError('')
    setGroupSelection(null)
    groupSelectionMarkerRef.current = null
    hostRef.current.innerHTML = ''

    const map = L.map(hostRef.current, {
      zoomControl: true,
      preferCanvas: true,
    })
    mapRef.current = map
    const mapContainer = map.getContainer()
    let restoreMapInteractions = null
    let cleanupLasso = null
    const bairrosPane = map.createPane('mesa-bairros-pane')
    const inundacaoPane = map.createPane('mesa-inundacao-pane')
    const marketPane = map.createPane('mesa-market-pane')
    const marketGroupPane = map.createPane('mesa-market-group-pane')
    const trabalhosPane = map.createPane('mesa-trabalhos-pane')
    const indicesPane = map.createPane('mesa-indices-pane')
    bairrosPane.style.zIndex = '410'
    inundacaoPane.style.zIndex = '412'
    marketPane.style.zIndex = '420'
    marketGroupPane.style.zIndex = '425'
    trabalhosPane.style.zIndex = '430'
    indicesPane.style.zIndex = '440'

    const canvasRenderer = L.canvas({ padding: 0.5, pane: 'mesa-market-pane' })
    const baseLayers = {}
    const overlayLayers = {}
    const overlayGroupsById = new Map()
    const dependencyAwareOverlays = []
    const hoverHighlightMetas = []

    ;(payload.tile_layers || []).forEach((layerDef, index) => {
      const tileLayer = L.tileLayer(String(layerDef?.url || ''), {
        attribution: String(layerDef?.attribution || '&copy; OpenStreetMap contributors'),
        crossOrigin: 'anonymous',
        detectRetina: true,
      })
      const label = String(layerDef?.label || layerDef?.id || `Base ${index + 1}`)
      baseLayers[label] = tileLayer
      if (index === 0) {
        tileLayer.addTo(map)
      }
    })
    const overlaySpecs = Array.isArray(payload.overlay_layers) && payload.overlay_layers.length
      ? payload.overlay_layers
      : buildLegacyOverlayLayers(payload)
    const responsivePointContainers = []

    const radiusBehavior = payload.radius_behavior || {}
    const minRadius = Number(radiusBehavior.min_radius) || 1.6
    const maxRadius = Number(radiusBehavior.max_radius) || 52.0
    const referenceZoom = Number(radiusBehavior.reference_zoom) || 12.0
    const growthFactor = Number(radiusBehavior.growth_factor) || 0.2

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

    function visitLayerTree(layer, visited, fn) {
      if (!layer || !layer._leaflet_id || visited.has(layer._leaflet_id)) return
      visited.add(layer._leaflet_id)
      fn(layer)
      if (typeof layer.eachLayer === 'function') {
        layer.eachLayer((child) => {
          visitLayerTree(child, visited, fn)
        })
      }
    }

    function applyResponsiveRadius() {
      const zoom = typeof map.getZoom === 'function' ? map.getZoom() : referenceZoom
      const zoomDelta = zoom - referenceZoom
      const expFactor = 2 ** (zoomDelta * growthFactor)
      let floorScale = 1.0
      if (zoomDelta >= 0) {
        floorScale = 1 + zoomDelta * 0.22
        if (zoom >= 15) {
          floorScale += (zoom - 14) * 0.30
        }
      } else {
        floorScale = Math.max(0.28, 1 + zoomDelta * 0.20)
      }

      responsivePointContainers.forEach((container) => {
        if (!container || typeof container.eachLayer !== 'function') return
        container.eachLayer((layer) => {
          const base = Number(layer.options?.mesaBaseRadius || layer.options?.radius || 4)
          const dynamicMin = Math.max(minRadius, base * floorScale)
          const dynamicMax = Math.max(dynamicMin + 0.1, Math.min(maxRadius, base * 8.0))
          const radius = clamp(base * expFactor, dynamicMin, dynamicMax)
          if (layer.options?.mesaGroupedMarketMarker && typeof layer.setIcon === 'function') {
            const iconSize = Math.max(16, Math.min(36, Math.round(radius * 2.35)))
            layer.setIcon(buildGroupedMarketIcon(layer.options.mesaGroupedMarketItem || {}, iconSize))
            return
          }
          if (typeof layer.setRadius !== 'function') return
          layer.setRadius(radius)
        })
      })
    }

    async function carregarPopupRegistro(popupRequest, layer) {
      const kind = String(popupRequest?.kind || '').trim()
      const rowId = Number(popupRequest?.row_id)
      const source = String(popupRequest?.source || '').trim()
      const endpoint = kind === 'elaboracao_row'
        ? '/api/elaboracao/map/popup'
        : '/api/visualizacao/map/popup'
      const cacheKey = `${kind || 'visualizacao_row'}:${source || 'default'}:${rowId}`
      const cached = popupCacheRef.current.get(cacheKey)
      if (cached) {
        const cachedHtml = typeof cached === 'string' ? cached : String(cached?.html || '')
        const cachedWidth = typeof cached === 'string' ? 420 : (Number(cached?.width) || 420)
        layer.unbindPopup()
        layer.bindPopup(cachedHtml, { maxWidth: cachedWidth }).openPopup()
        window.setTimeout(() => {
          const root = layer.getPopup()?.getElement()?.querySelector('[data-pager]')
          if (root) ensurePopupPager(root)
        }, 0)
        return
      }

      layer.bindPopup(
        '<div style="font-family:\'Segoe UI\'; color:#6c757d; font-size:12px;">Carregando detalhes...</div>',
        { maxWidth: 360 },
      ).openPopup()

      try {
        const body = {
          session_id: sessionId,
          row_id: rowId,
        }
        if (source) {
          body.source = source
        }
        const response = await fetch(apiUrl(endpoint), {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            ...(getAuthToken() ? { 'X-Auth-Token': getAuthToken() } : {}),
          },
          body: JSON.stringify(body),
        })
        const payloadResp = await readJsonSafely(response)
        if (!response.ok) {
          throw new Error(String(payloadResp?.detail || 'Falha ao carregar os dados do registro.'))
        }
        if (!payloadResp || typeof payloadResp !== 'object') {
          throw new Error('Resposta vazia ao carregar os dados do registro.')
        }
        const popupHtml = String(payloadResp?.popup_html || '')
        const popupWidth = Number(payloadResp?.popup_width) || 420
        if (!popupHtml.trim()) {
          throw new Error('Detalhes do registro indisponíveis para este ponto.')
        }
        popupCacheRef.current.set(cacheKey, { html: popupHtml, width: popupWidth })
        layer.unbindPopup()
        layer.bindPopup(popupHtml, { maxWidth: popupWidth }).openPopup()
        window.setTimeout(() => {
          const root = layer.getPopup()?.getElement()?.querySelector('[data-pager]')
          if (root) ensurePopupPager(root)
        }, 0)
      } catch (error) {
        layer.unbindPopup()
        layer.bindPopup(buildPopupErrorHtml(error?.message || 'Falha ao carregar os dados do registro.'), { maxWidth: 360 }).openPopup()
      }
    }

    popupLoaderRef.current = carregarPopupRegistro

    function handlePopupActionClick(event) {
      const target = event?.target
      if (!(target instanceof Element)) return
      const trigger = target.closest('[data-mesa-open-trabalho-tecnico="1"]')
      if (!trigger) return
      const trabalhoId = String(trigger.getAttribute('data-trabalho-id') || '').trim()
      if (!trabalhoId) return
      event.preventDefault()
      event.stopPropagation()
      emitOpenTrabalhoTecnicoMessage(
        trabalhoId,
        String(trigger.getAttribute('data-origem') || 'pesquisa_mapa').trim() || 'pesquisa_mapa',
      )
    }

    mapContainer?.addEventListener('click', handlePopupActionClick, true)

    function bindPointPopup(marker, item) {
      const popupHtml = String(item?.popup_html || '').trim()
      if (popupHtml) {
        marker.bindPopup(popupHtml, { maxWidth: Number(item?.popup_max_width) || 420 })
        return
      }

      const popupRequest = item?.popup_request
      const popupKind = String(popupRequest?.kind || '').trim()
      if (
        ['visualizacao_row', 'elaboracao_row'].includes(popupKind)
        && Number.isFinite(Number(popupRequest?.row_id))
        && sessionId
      ) {
        marker.on('click', () => {
          void carregarPopupRegistro(popupRequest, marker)
        })
      }
    }

    function rememberPathBaseStyle(layer) {
      if (!layer || !layer.options) return
      if (!Number.isFinite(layer.options.mesaBaseOpacity)) {
        layer.options.mesaBaseOpacity = Number.isFinite(layer.options.opacity) ? layer.options.opacity : 1
      }
      if (!Number.isFinite(layer.options.mesaBaseFillOpacity)) {
        layer.options.mesaBaseFillOpacity = Number.isFinite(layer.options.fillOpacity) ? layer.options.fillOpacity : 0
      }
      if (!Number.isFinite(layer.options.mesaBaseWeight)) {
        layer.options.mesaBaseWeight = Number.isFinite(layer.options.weight) ? layer.options.weight : 1
      }
      if (!layer.options.mesaBaseColor && layer.options.color) {
        layer.options.mesaBaseColor = layer.options.color
      }
      if (!layer.options.mesaBaseFillColor && layer.options.fillColor) {
        layer.options.mesaBaseFillColor = layer.options.fillColor
      }
    }

    function rememberMarkerBaseStyle(layer) {
      if (!layer || !layer.options) return
      if (!Number.isFinite(layer.options.mesaBaseMarkerOpacity)) {
        layer.options.mesaBaseMarkerOpacity = Number.isFinite(layer.options.opacity) ? layer.options.opacity : 1
      }
      if (!Number.isFinite(layer.options.mesaBaseFillOpacity)) {
        layer.options.mesaBaseFillOpacity = Number.isFinite(layer.options.fillOpacity) ? layer.options.fillOpacity : 0
      }
      if (!Number.isFinite(layer.options.mesaBaseRadius) && typeof layer.getRadius === 'function') {
        const radius = layer.getRadius()
        if (Number.isFinite(radius) && radius > 0) {
          layer.options.mesaBaseRadius = radius
        }
      }
    }

    function applyVisualState(layer, state) {
      if (!layer) return

      const isPath = typeof layer.setStyle === 'function'
      if (isPath) {
        rememberPathBaseStyle(layer)
        const baseOpacity = Number.isFinite(layer.options.mesaBaseOpacity) ? layer.options.mesaBaseOpacity : 1
        const baseFillOpacity = Number.isFinite(layer.options.mesaBaseFillOpacity) ? layer.options.mesaBaseFillOpacity : 0
        const baseWeight = Number.isFinite(layer.options.mesaBaseWeight) ? layer.options.mesaBaseWeight : 1
        const hoverFillOpacity = Number(layer.options.mesaHoverFillOpacity)

        let nextOpacity = baseOpacity
        let nextFillOpacity = baseFillOpacity
        let nextWeight = baseWeight
        if (state === 'dim') {
          nextOpacity = Math.max(0.28, baseOpacity * 0.45)
          nextFillOpacity = baseFillOpacity > 0 ? Math.max(0.06, baseFillOpacity * 0.55) : baseFillOpacity
          nextWeight = Math.max(1, baseWeight * 0.92)
        } else if (state === 'highlight') {
          nextOpacity = Math.max(baseOpacity, 0.98)
          nextFillOpacity = Number.isFinite(hoverFillOpacity)
            ? Math.max(baseFillOpacity, hoverFillOpacity)
            : (
              baseFillOpacity > 0
                ? Math.max(baseFillOpacity, Math.min(0.46, (baseFillOpacity * 2.3) + 0.12))
                : baseFillOpacity
            )
          nextWeight = Math.max(baseWeight + 1.2, baseWeight * 1.25)
        }

        layer.setStyle({
          opacity: nextOpacity,
          fill: layer.options.fill === true || Number.isFinite(hoverFillOpacity),
          fillOpacity: nextFillOpacity,
          weight: nextWeight,
          color: layer.options.mesaBaseColor || layer.options.color,
          fillColor: layer.options.mesaBaseFillColor || layer.options.fillColor || layer.options.mesaBaseColor || layer.options.color,
        })

        if (state === 'highlight' && typeof layer.bringToFront === 'function') {
          layer.bringToFront()
        }
      }

      if (typeof layer.setRadius === 'function') {
        rememberMarkerBaseStyle(layer)
        const baseRadius = Number.isFinite(layer.options.mesaBaseRadius) ? layer.options.mesaBaseRadius : null
        if (baseRadius !== null) {
          let nextRadius = baseRadius
          if (state === 'dim') {
            nextRadius = Math.max(1.6, baseRadius * 0.86)
          } else if (state === 'highlight') {
            nextRadius = Math.min(baseRadius * 1.32, baseRadius + 3.0)
          }
          layer.setRadius(nextRadius)
        }
        if (isPath && state === 'highlight' && typeof layer.bringToFront === 'function') {
          layer.bringToFront()
        }
      }

      if (!isPath && typeof layer.setOpacity === 'function') {
        rememberMarkerBaseStyle(layer)
        const baseOpacity = Number.isFinite(layer.options.mesaBaseMarkerOpacity) ? layer.options.mesaBaseMarkerOpacity : 1
        let nextOpacity = baseOpacity
        if (state === 'dim') {
          nextOpacity = Math.max(0.18, baseOpacity * 0.28)
        } else if (state === 'highlight') {
          nextOpacity = Math.max(baseOpacity, 1)
        }
        layer.setOpacity(nextOpacity)
      }
    }

    function applyGroupState(meta, state) {
      if (!meta?.layerGroup) return
      const visited = new Set()
      visitLayerTree(meta.layerGroup, visited, (current) => {
        if (current === meta.layerGroup) return
        applyVisualState(current, state)
      })
    }

    function clearHoverHighlight(groupKey) {
      const metas = hoverHighlightMetas.filter((entry) => entry.groupKey === groupKey)
      metas.forEach((meta) => {
        applyGroupState(meta, 'normal')
      })
    }

    function setHoveredHighlight(groupKey, activeId) {
      const metas = hoverHighlightMetas.filter((entry) => entry.groupKey === groupKey)
      const activeMeta = metas.find((entry) => entry.id === activeId) || null
      if (!activeMeta || !activeMeta.layerGroup || !map.hasLayer(activeMeta.layerGroup)) {
        clearHoverHighlight(groupKey)
        return
      }

      metas.forEach((meta) => {
        if (!meta.layerGroup || !map.hasLayer(meta.layerGroup)) {
          applyGroupState(meta, 'normal')
          return
        }
        applyGroupState(meta, meta.id === activeId ? 'highlight' : 'dim')
      })
    }

    function addPointMarkers(layerGroup, items) {
      if (!Array.isArray(items) || !items.length) return
      responsivePointContainers.push(layerGroup)
      items.forEach((item) => {
        const latlng = parseLatLonPair(item?.lat, item?.lon)
        if (!latlng) return
        if (item?.grouped) {
          if (isTrueFlag(item?.hide_count)) {
            const marker = L.circleMarker(latlng, {
              renderer: canvasRenderer,
              pane: String(item?.pane || 'mesa-market-pane'),
              radius: Number(item?.base_radius) || 4,
              color: String(item?.stroke_color || '#000000'),
              weight: Number.isFinite(Number(item?.stroke_weight))
                ? Number(item.stroke_weight)
                : (Number(item?.base_radius) > 4 ? 1 : 0.8),
              fill: item?.fill !== false,
              fillColor: String(item?.color || item?.fill_color || '#FF8C00'),
              fillOpacity: Number.isFinite(Number(item?.fill_opacity)) ? Number(item.fill_opacity) : 0.68,
              interactive: item?.interactive !== false,
              bubblingMouseEvents: false,
            })
            marker.options.mesaBaseRadius = Number(item?.base_radius) || 4
            const tooltipHtml = String(item?.tooltip_html || '').trim()
            if (tooltipHtml) {
              marker.bindTooltip(tooltipHtml, { sticky: item?.tooltip_sticky !== false })
            }
            marker.on('click', () => {
              map.closePopup()
              marker.unbindPopup()
              const groupItems = Array.isArray(item?.group_items) ? item.group_items : []
              if (!groupItems.length) return
              groupSelectionMarkerRef.current = marker
              setGroupSelection({
                title: String(item?.group_title || 'Dados neste local'),
                count: Number(item?.count) || groupItems.length,
                hideCount: isTrueFlag(item?.hide_group_count),
                items: groupItems,
              })
            })
            layerGroup.addLayer(marker)
            return
          }
          const marker = L.marker(latlng, {
            icon: buildGroupedMarketIcon(item, 16),
            pane: String(item?.pane || 'mesa-market-group-pane'),
            interactive: item?.interactive !== false,
            keyboard: item?.keyboard !== false,
            bubblingMouseEvents: false,
          })
          marker.options.mesaBaseRadius = Number(item?.base_radius) || 4
          marker.options.mesaGroupedMarketMarker = true
          marker.options.mesaGroupedMarketItem = item
          const tooltipHtml = String(item?.tooltip_html || '').trim()
          if (tooltipHtml) {
            marker.bindTooltip(tooltipHtml, { sticky: item?.tooltip_sticky !== false })
          }
          marker.on('click', () => {
            map.closePopup()
            marker.unbindPopup()
            const groupItems = Array.isArray(item?.group_items) ? item.group_items : []
            if (!groupItems.length) return
            groupSelectionMarkerRef.current = marker
            setGroupSelection({
              title: String(item?.group_title || `${groupItems.length} dados neste local`),
              count: Number(item?.count) || groupItems.length,
              hideCount: isTrueFlag(item?.hide_group_count),
              items: groupItems,
            })
          })
          layerGroup.addLayer(marker)
          return
        }
        const marker = L.circleMarker(latlng, {
          renderer: canvasRenderer,
          pane: String(item?.pane || 'mesa-market-pane'),
          radius: Number(item?.base_radius) || 4,
          color: String(item?.stroke_color || '#000000'),
          weight: Number.isFinite(Number(item?.stroke_weight))
            ? Number(item.stroke_weight)
            : (Number(item?.base_radius) > 4 ? 1 : 0.8),
          fill: item?.fill !== false,
          fillColor: String(item?.color || item?.fill_color || '#FF8C00'),
          fillOpacity: Number.isFinite(Number(item?.fill_opacity)) ? Number(item.fill_opacity) : 0.68,
          interactive: item?.interactive !== false,
          bubblingMouseEvents: false,
        })
        marker.options.mesaBaseRadius = Number(item?.base_radius) || 4
        const tooltipHtml = String(item?.tooltip_html || '').trim()
        if (tooltipHtml) {
          marker.bindTooltip(tooltipHtml, { sticky: item?.tooltip_sticky !== false })
        } else if (item?.tooltip) {
          marker.bindTooltip(buildTooltipHtml(item.tooltip), { sticky: item?.tooltip_sticky !== false })
        }
        bindPointPopup(marker, item)
        layerGroup.addLayer(marker)
      })
    }

    function addMarkerOverlays(layerGroup, items) {
      if (!Array.isArray(items) || !items.length) return
      items.forEach((item) => {
        const latlng = parseLatLonPair(item?.lat, item?.lon)
        if (!latlng) return
        const icon = L.divIcon({
          html: String(item?.marker_html || ''),
          iconSize: Array.isArray(item?.icon_size) ? item.icon_size : [14, 14],
          iconAnchor: Array.isArray(item?.icon_anchor) ? item.icon_anchor : [7, 7],
          className: String(item?.class_name || 'mesa-map-marker'),
        })
        const marker = L.marker(latlng, {
          icon,
          pane: String(item?.pane || (String(item?.class_name || '').includes('indice') ? 'mesa-indices-pane' : 'mesa-trabalhos-pane')),
          bubblingMouseEvents: item?.bubbling_mouse_events === true,
          interactive: item?.interactive !== false,
          keyboard: item?.keyboard !== false,
        })
        if (item?.ignore_bounds) {
          marker.options.mesaIgnoreBounds = true
        }
        if (item?.tooltip_html) {
          marker.bindTooltip(String(item.tooltip_html), { sticky: item?.tooltip_sticky !== false })
        }
        if (item?.popup_html) {
          marker.bindPopup(String(item.popup_html), { maxWidth: Number(item?.popup_max_width) || 360 })
        }
        layerGroup.addLayer(marker)
      })
    }

    function filterDependencyAwareMarkers(items) {
      if (!Array.isArray(items) || !items.length) return []
      return items.filter((item) => {
        const sourceIds = Array.isArray(item?.source_overlay_ids)
          ? item.source_overlay_ids.map((entry) => String(entry || '').trim()).filter(Boolean)
          : []
        if (!sourceIds.length) return true
        return sourceIds.some((sourceId) => {
          const sourceLayer = overlayGroupsById.get(sourceId)
          return Boolean(sourceLayer && map.hasLayer(sourceLayer))
        })
      })
    }

    function areRequiredOverlaysVisible(spec) {
      const requiredIds = Array.isArray(spec?.required_overlay_ids)
        ? spec.required_overlay_ids.map((entry) => String(entry || '').trim()).filter(Boolean)
        : []
      if (!requiredIds.length) return true
      return requiredIds.every((requiredId) => {
        const requiredLayer = overlayGroupsById.get(requiredId)
        return Boolean(requiredLayer && map.hasLayer(requiredLayer))
      })
    }

    function renderDependencyAwareOverlay(spec, layerGroup) {
      if (!layerGroup) return
      layerGroup.clearLayers()
      if (!map.hasLayer(layerGroup)) return
      if (!areRequiredOverlaysVisible(spec)) return
      const specId = String(spec?.id || '').trim()
      const hoverGroup = String(spec?.hover_highlight_group || '').trim()
      const hoverHighlight = hoverGroup && specId ? { groupKey: hoverGroup, id: specId } : null
      addShapeOverlays(layerGroup, spec?.shapes, hoverHighlight)
      addMarkerOverlays(layerGroup, filterDependencyAwareMarkers(spec?.markers))
    }

    function refreshDependencyAwareOverlays() {
      dependencyAwareOverlays.forEach(({ spec, layerGroup }) => {
        renderDependencyAwareOverlay(spec, layerGroup)
      })
    }

    function addShapeOverlays(layerGroup, shapes, hoverHighlight = null) {
      if (!Array.isArray(shapes) || !shapes.length) return
      shapes.forEach((shape) => {
        const shapeType = String(shape?.type || shape?.shape_type || '').trim().toLowerCase()
        const style = {
          color: String(shape?.color || '#1f6fb2'),
          weight: Number.isFinite(Number(shape?.weight)) ? Number(shape.weight) : 2,
          opacity: Number.isFinite(Number(shape?.opacity)) ? Number(shape.opacity) : 0.8,
          fill: shape?.fill === true,
          fillColor: String(shape?.fill_color || shape?.color || '#1f6fb2'),
          fillOpacity: Number.isFinite(Number(shape?.fill_opacity)) ? Number(shape.fill_opacity) : 0.12,
          dashArray: shape?.dash_array ? String(shape.dash_array) : undefined,
          pane: String(shape?.pane || 'mesa-bairros-pane'),
        }
        let layer = null

        if (shapeType === 'circle' && Array.isArray(shape?.center) && shape.center.length === 2) {
          const center = parseLatLonPair(shape.center[0], shape.center[1])
          if (center) {
            layer = L.circle(center, { ...style, radius: Number(shape?.radius_m) || 0 })
          }
        } else if (shapeType === 'polyline' && Array.isArray(shape?.coords) && shape.coords.length) {
          layer = L.polyline(shape.coords, style)
        } else if (shapeType === 'polygon' && Array.isArray(shape?.coords) && shape.coords.length) {
          layer = L.polygon(shape.coords, style)
        } else if (shapeType === 'circlemarker' && Array.isArray(shape?.center) && shape.center.length === 2) {
          const center = parseLatLonPair(shape.center[0], shape.center[1])
          if (center) {
            layer = L.circleMarker(center, { ...style, radius: Number(shape?.radius) || 6 })
          }
        }

        if (!layer) return
        const hoverFillOpacity = shape?.hover_fill_opacity === false ? NaN : Number(shape?.hover_fill_opacity)
        if (Number.isFinite(hoverFillOpacity)) {
          layer.options.mesaHoverFillOpacity = hoverFillOpacity
        }
        if (shape?.ignore_bounds) {
          layer.options.mesaIgnoreBounds = true
        }
        if (shape?.tooltip_html) {
          layer.bindTooltip(String(shape.tooltip_html), { sticky: shape?.tooltip_sticky !== false })
        }
        if (shape?.popup_html) {
          layer.bindPopup(String(shape.popup_html), { maxWidth: Number(shape?.popup_max_width) || 360 })
        }
        if (hoverHighlight?.groupKey && hoverHighlight?.id && typeof layer.setStyle === 'function') {
          layer.on('mouseover', () => setHoveredHighlight(hoverHighlight.groupKey, hoverHighlight.id))
          layer.on('mouseout', () => clearHoverHighlight(hoverHighlight.groupKey))
        } else if (shapeType === 'polygon' && style.fill === true && Number.isFinite(hoverFillOpacity)) {
          layer.on('mouseover', () => applyVisualState(layer, 'highlight'))
          layer.on('mouseout', () => applyVisualState(layer, 'normal'))
        }
        layerGroup.addLayer(layer)
      })
    }

    function addGeoJsonOverlay(layerGroup, spec) {
      const geojsonLayer = L.geoJSON(null, {
        pane: String(spec?.geojson_pane || 'mesa-bairros-pane'),
        style: () => ({
          color: String(spec?.geojson_style?.color || '#4c6882'),
          weight: Number.isFinite(Number(spec?.geojson_style?.weight)) ? Number(spec.geojson_style.weight) : 1.0,
          opacity: Number.isFinite(Number(spec?.geojson_style?.opacity))
            ? Number(spec.geojson_style.opacity)
            : 0.88,
          fillColor: String(spec?.geojson_style?.fillColor || '#f39c12'),
          fillOpacity: Number.isFinite(Number(spec?.geojson_style?.fillOpacity))
            ? Number(spec.geojson_style.fillOpacity)
            : 0.04,
        }),
        onEachFeature: (feature, layer) => {
          const props = feature?.properties || {}
          const candidates = Array.isArray(spec?.geojson_tooltip_properties) ? spec.geojson_tooltip_properties : []
          const value = candidates.map((key) => props?.[key]).find((entry) => String(entry || '').trim())
          if (value) {
            const prefix = String(spec?.geojson_tooltip_label || '').trim()
            layer.bindTooltip(prefix ? `${prefix}: ${String(value)}` : String(value), { sticky: false })
          }
        },
      })
      layerGroup.addLayer(geojsonLayer)

      if (spec?.geojson_data) {
        geojsonLayer.addData(spec.geojson_data)
        return
      }
      if (!spec?.geojson_url) return
      let carregada = false
      let carregando = false
      const carregarQuandoNecessario = () => {
        if (carregada || carregando || disposed) return
        carregando = true
        void carregarGeojson(spec.geojson_url)
          .then((geojson) => {
            if (disposed || !geojson) return
            geojsonLayer.addData(geojson)
            carregada = true
          })
          .catch((error) => {
            // A camada é opcional, mas o aviso facilita diagnosticar uma falha
            // de rede ou de autenticação sem impedir o uso do mapa.
            console.warn('Não foi possível carregar a camada vetorial.', error)
          })
          .finally(() => {
            carregando = false
          })
      }

      // O evento da própria camada é disparado sempre que ela entra no mapa,
      // inclusive por controles ou fluxos que não disparam `overlayadd`.
      layerGroup.on('add', carregarQuandoNecessario)
      if (map.hasLayer(layerGroup)) carregarQuandoNecessario()
      map.on('overlayadd', (event) => {
        if (event?.layer === layerGroup) carregarQuandoNecessario()
      })
    }

    function addHeatmapOverlay(layerGroup, heatmapSpec) {
      if (!heatmapSpec || typeof L.heatLayer !== 'function') return
      const points = Array.isArray(heatmapSpec?.points)
        ? heatmapSpec.points
          .map((item) => {
            const latlng = parseLatLonPair(item?.lat, item?.lon)
            const weight = Number(item?.weight)
            if (!latlng) return null
            if (Number.isFinite(weight)) return [latlng[0], latlng[1], weight]
            return latlng
          })
          .filter(Boolean)
        : []
      if (!points.length) return
      const layer = L.heatLayer(points, {
        radius: Number(heatmapSpec?.radius) || 20,
        blur: Number(heatmapSpec?.blur) || 18,
        minOpacity: Number.isFinite(Number(heatmapSpec?.min_opacity)) ? Number(heatmapSpec.min_opacity) : 0.28,
        maxZoom: Number(heatmapSpec?.max_zoom) || 17,
        gradient: heatmapSpec?.gradient || undefined,
      })
      layerGroup.addLayer(layer)
    }

    overlaySpecs.forEach((spec, index) => {
      const specId = String(spec?.id || '').trim()
      const label = String(spec?.label || spec?.id || `Camada ${index + 1}`)
      const layerGroup = L.layerGroup()
      overlayLayers[label] = layerGroup
      if (specId) {
        overlayGroupsById.set(specId, layerGroup)
      }
      if (!isFalseFlag(spec?.show)) {
        layerGroup.addTo(map)
      }
      if (spec?.geojson_url || spec?.geojson_data) {
        addGeoJsonOverlay(layerGroup, spec)
      }
      addHeatmapOverlay(layerGroup, spec?.heatmap)
      addPointMarkers(layerGroup, spec?.points)
      const hasMarkerDependencies = Array.isArray(spec?.markers)
        && spec.markers.some((item) => Array.isArray(item?.source_overlay_ids) && item.source_overlay_ids.length)
      const hasOverlayDependencies = Array.isArray(spec?.required_overlay_ids) && spec.required_overlay_ids.length > 0
      const hoverGroup = String(spec?.hover_highlight_group || '').trim()
      const hoverHighlight = hoverGroup && specId ? { groupKey: hoverGroup, id: specId } : null
      if (hasMarkerDependencies || hasOverlayDependencies) {
        dependencyAwareOverlays.push({ spec, layerGroup })
        renderDependencyAwareOverlay(spec, layerGroup)
      } else {
        addShapeOverlays(layerGroup, spec?.shapes, hoverHighlight)
        addMarkerOverlays(layerGroup, spec?.markers)
      }

      if (hoverGroup && specId) {
        hoverHighlightMetas.push({
          id: specId,
          groupKey: hoverGroup,
          layerGroup,
        })
      }
    })

    if (dependencyAwareOverlays.length) {
      map.on('overlayadd', refreshDependencyAwareOverlays)
      map.on('overlayremove', refreshDependencyAwareOverlays)
    }

    function syncInitialOverlayVisibility(layerControl = null) {
      overlaySpecs.forEach((spec) => {
        const label = String(spec?.label || spec?.id || '').trim()
        if (!label) return
        const layerGroup = overlayLayers[label]
        if (!layerGroup) return
        if (isFalseFlag(spec?.show)) {
          map.removeLayer(layerGroup)
        }
      })

      const container = layerControl?.getContainer?.()
      if (!container) return
      const inputs = Array.from(container.querySelectorAll('.leaflet-control-layers-overlays input[type="checkbox"]'))
      inputs.forEach((input) => {
        const layerId = String(input?.layerId || '')
        const matchingLabel = Object.entries(overlayLayers).find(([, layer]) => String(L.Util.stamp(layer)) === layerId)?.[0]
        if (!matchingLabel) return
        const spec = overlaySpecs.find((item) => String(item?.label || item?.id || '').trim() === matchingLabel)
        if (isFalseFlag(spec?.show)) {
          input.checked = false
        }
      })
    }

    function addLayerControlCloseButton(layerControl) {
      const container = layerControl?.getContainer?.()
      if (!container || container.querySelector('.mesa-layer-control-close')) return
      const button = L.DomUtil.create('button', 'mesa-layer-control-close', container)
      button.type = 'button'
      button.setAttribute('aria-label', 'Fechar camadas')
      button.title = 'Fechar camadas'
      button.textContent = 'Fechar'
      L.DomEvent.disableClickPropagation(button)
      L.DomEvent.on(button, 'click', (event) => {
        L.DomEvent.stop(event)
        setLayerControlOpen(layerControl, false)
      })
    }

    function updateLayerControlToggleState(layerControl) {
      const container = layerControl?.getContainer?.()
      const toggle = layerControl?._layersLink || container?.querySelector?.('.leaflet-control-layers-toggle')
      if (!container || !toggle) return
      toggle.setAttribute('aria-expanded', container.classList.contains('leaflet-control-layers-expanded') ? 'true' : 'false')
    }

    function setLayerControlOpen(layerControl, open) {
      const container = layerControl?.getContainer?.()
      if (!container) return
      if (open && typeof layerControl.expand === 'function') {
        layerControl.expand()
      } else if (!open && typeof layerControl.collapse === 'function') {
        layerControl.collapse()
      } else {
        container.classList.toggle('leaflet-control-layers-expanded', !!open)
      }
      updateLayerControlToggleState(layerControl)
    }

    function enableLayerControlClickToggle(layerControl) {
      const container = layerControl?.getContainer?.()
      if (!container) return
      container.classList.add('mesa-layer-control-click-toggle')
      if (layerControl?._map && typeof layerControl.collapse === 'function') {
        layerControl._map.off('click', layerControl.collapse, layerControl)
      }
      if (typeof layerControl?._expandSafely === 'function') {
        L.DomEvent.off(container, 'mouseenter', layerControl._expandSafely, layerControl)
      }
      if (typeof layerControl?.collapse === 'function') {
        L.DomEvent.off(container, 'mouseleave', layerControl.collapse, layerControl)
      }

      const currentToggle = layerControl?._layersLink || container.querySelector('.leaflet-control-layers-toggle')
      if (!currentToggle || currentToggle.dataset.mesaClickToggle === '1') {
        updateLayerControlToggleState(layerControl)
        return
      }

      const toggle = currentToggle.cloneNode(true)
      toggle.dataset.mesaClickToggle = '1'
      toggle.href = '#'
      toggle.title = 'Camadas'
      toggle.setAttribute('role', 'button')
      toggle.setAttribute('aria-label', 'Abrir ou fechar camadas')
      currentToggle.parentNode.replaceChild(toggle, currentToggle)
      layerControl._layersLink = toggle

      const togglePanel = (event) => {
        L.DomEvent.stop(event)
        const expanded = container.classList.contains('leaflet-control-layers-expanded')
        setLayerControlOpen(layerControl, !expanded)
      }

      L.DomEvent.disableClickPropagation(toggle)
      L.DomEvent.on(toggle, 'click', togglePanel)
      L.DomEvent.on(toggle, 'keydown', (event) => {
        if (event.key !== 'Enter' && event.key !== ' ' && event.keyCode !== 13 && event.keyCode !== 32) return
        togglePanel(event)
      })
      updateLayerControlToggleState(layerControl)
    }

    syncInitialOverlayVisibility()

    if (payload.controls?.layer_control) {
      const collapsed = !isFalseFlag(payload.controls?.layer_control_collapsed)
      const layerControl = L.control.layers(baseLayers, overlayLayers, { collapsed }).addTo(map)
      enableLayerControlClickToggle(layerControl)
      addLayerControlCloseButton(layerControl)
      syncInitialOverlayVisibility(layerControl)
    }
    if (payload.controls?.measure) {
      const disableInteractionsForMeasure = () => {
        if (restoreMapInteractions) return
        const states = {
          dragging: !!map.dragging?.enabled?.(),
          doubleClickZoom: !!map.doubleClickZoom?.enabled?.(),
          boxZoom: !!map.boxZoom?.enabled?.(),
          keyboard: !!map.keyboard?.enabled?.(),
          touchZoom: !!map.touchZoom?.enabled?.(),
          scrollWheelZoom: !!map.scrollWheelZoom?.enabled?.(),
        }
        map.dragging?.disable?.()
        map.doubleClickZoom?.disable?.()
        map.boxZoom?.disable?.()
        map.keyboard?.disable?.()
        map.touchZoom?.disable?.()
        map.scrollWheelZoom?.disable?.()
        if (map.getContainer()) {
          map.getContainer().style.cursor = 'crosshair'
        }
        restoreMapInteractions = () => {
          if (states.dragging) map.dragging?.enable?.()
          if (states.doubleClickZoom) map.doubleClickZoom?.enable?.()
          if (states.boxZoom) map.boxZoom?.enable?.()
          if (states.keyboard) map.keyboard?.enable?.()
          if (states.touchZoom) map.touchZoom?.enable?.()
          if (states.scrollWheelZoom) map.scrollWheelZoom?.enable?.()
          if (map.getContainer()) {
            map.getContainer().style.cursor = ''
          }
          restoreMapInteractions = null
        }
      }
      const measureLayerGroup = L.layerGroup().addTo(map)
      const measureState = {
        panelOpen: false,
        active: false,
        points: [],
      }

      let measureRoot = null
      let measureInteraction = null
      let measureHint = null
      let measureResults = null
      let measureStartButton = null
      let measureCancelButton = null
      let measureFinishButton = null
      let measureCloseButton = null

      function renderMeasureGeometry() {
        measureLayerGroup.clearLayers()
        if (!measureState.points.length) return

        if (measureState.points.length >= 2) {
          L.polyline(measureState.points, {
            color: '#1f6fb2',
            weight: 3,
            opacity: 0.9,
            dashArray: measureState.active ? '8 6' : undefined,
          }).addTo(measureLayerGroup)
        }

        if (!measureState.active && measureState.points.length >= 3) {
          L.polygon(measureState.points, {
            color: '#1f6fb2',
            weight: 2,
            opacity: 0.88,
            fillColor: '#1f6fb2',
            fillOpacity: 0.12,
          }).addTo(measureLayerGroup)
        }

        measureState.points.forEach((latlng) => {
          L.circleMarker(latlng, {
            radius: 5,
            color: '#ffffff',
            weight: 2,
            fillColor: '#1f6fb2',
            fillOpacity: 1,
            pane: 'mesa-indices-pane',
          }).addTo(measureLayerGroup)
        })
      }

      function renderMeasurePanel() {
        if (!measureRoot || !measureInteraction || !measureHint || !measureResults) return
        measureRoot.classList.toggle('leaflet-control-measure-expanded', measureState.panelOpen)
        measureInteraction.style.display = measureState.panelOpen ? 'block' : 'none'
        if (!measureState.panelOpen) return

        const pointCount = measureState.points.length
        const lastPoint = pointCount ? measureState.points[pointCount - 1] : null
        const distance = pointCount >= 2
          ? measureState.points.reduce((total, point, index, list) => {
            if (index === 0) return total
            return total + map.distance(list[index - 1], point)
          }, 0)
          : 0
        const area = !measureState.active && pointCount >= 3 ? computePolygonArea(measureState.points) : 0

        if (measureState.active) {
          measureHint.textContent = pointCount
            ? 'Clique no mapa para adicionar pontos. Dê duplo clique ou finalize para encerrar.'
            : 'Clique no mapa para iniciar a medição.'
        } else if (pointCount) {
          measureHint.textContent = 'Medição concluída.'
        } else {
          measureHint.textContent = 'Inicie uma nova medição.'
        }

        const parts = []
        if (lastPoint) {
          parts.push(
            '<div class="mesa-leaflet-measure-row">'
            + '<strong>Último ponto</strong>'
            + `<span>${escapeHtml(formatLatLng(lastPoint))}</span>`
            + `<span>${escapeHtml(formatDecimalLatLng(lastPoint))}</span>`
            + '</div>',
          )
        }
        if (distance > 0) {
          parts.push(
            '<div class="mesa-leaflet-measure-row">'
            + '<strong>Distância total</strong>'
            + `<span>${escapeHtml(formatDistance(distance))}</span>`
            + '</div>',
          )
        }
        if (area > 0) {
          parts.push(
            '<div class="mesa-leaflet-measure-row">'
            + '<strong>Área</strong>'
            + `<span>${escapeHtml(formatArea(area))}</span>`
            + '</div>',
          )
        }
        measureResults.innerHTML = parts.join('')
        measureResults.style.display = parts.length ? 'block' : 'none'
        measureStartButton.style.display = !measureState.active ? 'inline-flex' : 'none'
        measureStartButton.textContent = measureState.points.length ? 'Nova medição' : 'Criar nova medição'
        measureCancelButton.style.display = measureState.active || measureState.points.length ? 'inline-flex' : 'none'
        measureFinishButton.style.display = measureState.active && measureState.points.length >= 2 ? 'inline-flex' : 'none'
        measureCloseButton.style.display = !measureState.active ? 'inline-flex' : 'none'
      }

      function resetMeasureState({ closePanel = false } = {}) {
        measureState.active = false
        measureState.points = []
        measureLayerGroup.clearLayers()
        restoreMapInteractions?.()
        if (closePanel) {
          measureState.panelOpen = false
        }
        renderMeasurePanel()
      }

      function startMeasurement() {
        measureState.panelOpen = true
        measureState.active = true
        measureState.points = []
        measureLayerGroup.clearLayers()
        disableInteractionsForMeasure()
        renderMeasurePanel()
      }

      function finishMeasurement() {
        if (!measureState.active) return
        measureState.active = false
        restoreMapInteractions?.()
        renderMeasureGeometry()
        renderMeasurePanel()
      }

      function onMeasureMapClick(event) {
        if (!measureState.active || !event?.latlng) return
        const lastPoint = measureState.points[measureState.points.length - 1]
        if (lastPoint && typeof event.latlng.equals === 'function' && event.latlng.equals(lastPoint)) return
        measureState.points = [...measureState.points, event.latlng]
        renderMeasureGeometry()
        renderMeasurePanel()
      }

      function onMeasureMapDblClick(event) {
        if (!measureState.active) return
        L.DomEvent.stop(event)
        finishMeasurement()
      }

      map.on('click', onMeasureMapClick)
      map.on('dblclick', onMeasureMapDblClick)

      const measureControl = L.control({ position: 'topright' })
      measureControl.onAdd = () => {
        measureRoot = L.DomUtil.create('div', 'leaflet-control-measure mesa-leaflet-measure leaflet-bar leaflet-control')
        measureRoot.setAttribute('aria-haspopup', 'true')

        const toggle = L.DomUtil.create('a', 'leaflet-control-measure-toggle mesa-leaflet-measure-toggle', measureRoot)
        toggle.href = '#'
        toggle.title = 'Medir distâncias'
        toggle.setAttribute('aria-label', 'Medir distâncias')
        toggle.innerHTML = '<span class="mesa-sr-only">Medir distâncias</span>'

        measureInteraction = L.DomUtil.create('div', 'leaflet-control-measure-interaction mesa-leaflet-measure-interaction', measureRoot)
        measureInteraction.style.display = 'none'

        const title = L.DomUtil.create('h3', 'mesa-leaflet-measure-title', measureInteraction)
        title.textContent = 'Medir distâncias'

        measureHint = L.DomUtil.create('p', 'mesa-leaflet-measure-hint', measureInteraction)
        measureResults = L.DomUtil.create('div', 'mesa-leaflet-measure-results', measureInteraction)

        const actions = L.DomUtil.create('div', 'mesa-leaflet-measure-actions', measureInteraction)
        measureStartButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn is-primary', actions)
        measureStartButton.type = 'button'
        measureStartButton.textContent = 'Criar nova medição'

        measureCancelButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn', actions)
        measureCancelButton.type = 'button'
        measureCancelButton.textContent = 'Cancelar'

        measureFinishButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn', actions)
        measureFinishButton.type = 'button'
        measureFinishButton.textContent = 'Finalizar'

        measureCloseButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn', actions)
        measureCloseButton.type = 'button'
        measureCloseButton.textContent = 'Fechar'

        L.DomEvent.disableClickPropagation(measureRoot)
        L.DomEvent.disableScrollPropagation(measureRoot)
        L.DomEvent.on(toggle, 'click', (event) => {
          L.DomEvent.stop(event)
          if (measureState.active) return
          measureState.panelOpen = !measureState.panelOpen
          renderMeasurePanel()
        })
        L.DomEvent.on(measureStartButton, 'click', (event) => {
          L.DomEvent.stop(event)
          startMeasurement()
        })
        L.DomEvent.on(measureCancelButton, 'click', (event) => {
          L.DomEvent.stop(event)
          resetMeasureState()
        })
        L.DomEvent.on(measureFinishButton, 'click', (event) => {
          L.DomEvent.stop(event)
          finishMeasurement()
        })
        L.DomEvent.on(measureCloseButton, 'click', (event) => {
          L.DomEvent.stop(event)
          if (measureState.active) return
          measureState.panelOpen = false
          renderMeasurePanel()
        })

        renderMeasurePanel()
        return measureRoot
      }
      measureControl.addTo(map)
    }

    if (payload.controls?.market_lasso) {
      const lassoLayerGroup = L.layerGroup().addTo(map)
      const lassoState = {
        panelOpen: false,
        active: false,
        drawing: false,
        points: [],
        selectedModels: [],
        error: '',
        downloading: false,
      }

      let lassoRoot = null
      let lassoInteraction = null
      let lassoHint = null
      let lassoResults = null
      let lassoStartButton = null
      let lassoCancelButton = null
      let lassoDownloadButton = null
      let lassoCloseButton = null

      function disableInteractionsForLasso() {
        if (restoreMapInteractions) return
        const states = {
          dragging: !!map.dragging?.enabled?.(),
          doubleClickZoom: !!map.doubleClickZoom?.enabled?.(),
          boxZoom: !!map.boxZoom?.enabled?.(),
          keyboard: !!map.keyboard?.enabled?.(),
          touchZoom: !!map.touchZoom?.enabled?.(),
          scrollWheelZoom: !!map.scrollWheelZoom?.enabled?.(),
        }
        map.dragging?.disable?.()
        map.doubleClickZoom?.disable?.()
        map.boxZoom?.disable?.()
        map.keyboard?.disable?.()
        map.touchZoom?.disable?.()
        map.scrollWheelZoom?.disable?.()
        if (map.getContainer()) {
          map.getContainer().style.cursor = 'crosshair'
        }
        restoreMapInteractions = () => {
          if (states.dragging) map.dragging?.enable?.()
          if (states.doubleClickZoom) map.doubleClickZoom?.enable?.()
          if (states.boxZoom) map.boxZoom?.enable?.()
          if (states.keyboard) map.keyboard?.enable?.()
          if (states.touchZoom) map.touchZoom?.enable?.()
          if (states.scrollWheelZoom) map.scrollWheelZoom?.enable?.()
          if (map.getContainer()) {
            map.getContainer().style.cursor = ''
          }
          restoreMapInteractions = null
        }
      }

      function renderLassoGeometry() {
        lassoLayerGroup.clearLayers()
        if (!lassoState.points.length) return
        if (lassoState.points.length >= 2) {
          L.polyline(lassoState.points, {
            color: '#0f766e',
            weight: 2.6,
            opacity: 0.95,
            dashArray: lassoState.drawing ? '7 5' : undefined,
          }).addTo(lassoLayerGroup)
        }
        if (!lassoState.drawing && lassoState.points.length >= 3) {
          L.polygon(lassoState.points, {
            color: '#0f766e',
            weight: 2,
            opacity: 0.95,
            fillColor: '#0f766e',
            fillOpacity: 0.12,
          }).addTo(lassoLayerGroup)
        }
      }

      function getSelectedMarketModels(points) {
        if (!Array.isArray(points) || points.length < 3) return []
        const polygonPoints = points.map((latlng) => map.latLngToLayerPoint(latlng))
        const selectedByModel = new Map()

        overlaySpecs.forEach((spec) => {
          const specId = String(spec?.id || '').trim()
          const modelLayer = specId ? overlayGroupsById.get(specId) : null
          if (!Array.isArray(spec?.points) || !spec.points.length) return
          if (modelLayer && !map.hasLayer(modelLayer)) return

          spec.points.forEach((item) => {
            const latlng = parseLatLonPair(item?.lat, item?.lon)
            if (!latlng) return
            const layerPoint = map.latLngToLayerPoint(L.latLng(latlng[0], latlng[1]))
            if (!pointInPolygon(layerPoint, polygonPoints)) return

            const modelKey = String(item?.selection_model_id || specId || spec?.label || 'modelo')
            const modelName = String(item?.selection_model_name || spec?.label || modelKey)
            const indices = Array.isArray(item?.selection_indices) && item.selection_indices.length
              ? item.selection_indices
              : [item?.indice]
            const entry = selectedByModel.get(modelKey) || {
              nome: modelName,
              indices: [],
              seen: new Set(),
            }
            indices.forEach((indice) => {
              const text = String(indice ?? '').trim()
              if (!text || entry.seen.has(text)) return
              entry.seen.add(text)
              entry.indices.push(text)
            })
            selectedByModel.set(modelKey, entry)
          })
        })

        return Array.from(selectedByModel.values())
          .map((entry) => ({
            nome: entry.nome,
            indices: entry.indices.sort(naturalCompare),
          }))
          .filter((entry) => entry.indices.length)
      }

      function selectedTotal(models) {
        return (models || []).reduce((total, item) => total + (item.indices?.length || 0), 0)
      }

      function renderLassoPanel() {
        if (!lassoRoot || !lassoInteraction || !lassoHint || !lassoResults) return
        lassoRoot.classList.toggle('leaflet-control-measure-expanded', lassoState.panelOpen)
        lassoRoot.classList.toggle('is-active', lassoState.active)
        lassoInteraction.style.display = lassoState.panelOpen ? 'block' : 'none'
        if (!lassoState.panelOpen) return

        const total = selectedTotal(lassoState.selectedModels)
        const hasSelection = total > 0
        const hasAttempt = lassoState.points.length > 0 || !!lassoState.error

        if (lassoState.error) {
          lassoHint.textContent = lassoState.error
        } else if (lassoState.active) {
          lassoHint.textContent = 'Arraste no mapa para contornar os dados. Solte para concluir.'
        } else if (lassoState.selectedModels.length) {
          lassoHint.textContent = `${total.toLocaleString('pt-BR')} INDICE(s) selecionado(s).`
        } else {
          lassoHint.textContent = 'Crie um laço sobre os dados de mercado.'
        }

        lassoResults.innerHTML = lassoState.selectedModels.length
          ? lassoState.selectedModels.map((item) => (
            '<div class="mesa-leaflet-measure-row">'
            + `<strong>${escapeHtml(item.nome)}</strong>`
            + `<span>${escapeHtml(String(item.indices.length))} INDICE(s)</span>`
            + '</div>'
          )).join('')
          : ''
        lassoResults.style.display = lassoState.selectedModels.length ? 'block' : 'none'
        lassoStartButton.style.display = lassoState.active ? 'none' : 'inline-flex'
        lassoStartButton.textContent = hasSelection ? 'Novo laço' : (hasAttempt ? 'Tentar novamente' : 'Criar laço')
        lassoStartButton.disabled = lassoState.downloading
        lassoStartButton.style.order = hasSelection ? '2' : '1'
        lassoCancelButton.style.display = lassoState.active ? 'inline-flex' : 'none'
        lassoCancelButton.textContent = 'Cancelar'
        lassoCancelButton.disabled = lassoState.downloading
        lassoCancelButton.style.order = '1'
        lassoDownloadButton.style.display = hasSelection && !lassoState.active ? 'inline-flex' : 'none'
        lassoDownloadButton.disabled = lassoState.downloading
        lassoDownloadButton.textContent = lassoState.downloading ? 'Baixando...' : 'Baixar XLSX'
        lassoDownloadButton.style.order = '1'
        lassoCloseButton.style.display = !lassoState.active ? 'inline-flex' : 'none'
        lassoCloseButton.disabled = lassoState.downloading
        lassoCloseButton.style.order = '3'
      }

      function resetLassoState({ closePanel = false } = {}) {
        lassoState.active = false
        lassoState.drawing = false
        lassoState.points = []
        lassoState.selectedModels = []
        lassoState.error = ''
        lassoLayerGroup.clearLayers()
        restoreMapInteractions?.()
        if (closePanel) {
          lassoState.panelOpen = false
        }
        renderLassoPanel()
      }

      function startLasso() {
        lassoState.panelOpen = true
        lassoState.active = true
        lassoState.drawing = false
        lassoState.points = []
        lassoState.selectedModels = []
        lassoState.error = ''
        lassoLayerGroup.clearLayers()
        disableInteractionsForLasso()
        renderLassoPanel()
      }

      function finishLasso() {
        if (!lassoState.active) return
        lassoState.active = false
        lassoState.drawing = false
        restoreMapInteractions?.()
        if (lassoState.points.length < 3) {
          lassoState.error = 'Desenhe uma área maior para selecionar.'
          lassoState.selectedModels = []
          renderLassoGeometry()
          renderLassoPanel()
          return
        }
        lassoState.selectedModels = getSelectedMarketModels(lassoState.points)
        if (!lassoState.selectedModels.length) {
          lassoState.error = 'Nenhum dado de mercado selecionado.'
        }
        renderLassoGeometry()
        renderLassoPanel()
      }

      function onLassoMouseDown(event) {
        if (!lassoState.active || !event?.latlng) return
        L.DomEvent.stop(event)
        lassoState.drawing = true
        lassoState.points = [event.latlng]
        lassoState.selectedModels = []
        lassoState.error = ''
        renderLassoGeometry()
        renderLassoPanel()
      }

      function onLassoMouseMove(event) {
        if (!lassoState.active || !lassoState.drawing || !event?.latlng) return
        const lastPoint = lassoState.points[lassoState.points.length - 1]
        if (lastPoint && map.distance(lastPoint, event.latlng) < 2) return
        lassoState.points = [...lassoState.points, event.latlng]
        renderLassoGeometry()
      }

      function onLassoMouseUp(event) {
        if (!lassoState.active || !lassoState.drawing) return
        L.DomEvent.stop(event)
        finishLasso()
      }

      async function downloadLassoSelection() {
        if (!lassoState.selectedModels.length || lassoState.downloading) return
        lassoState.downloading = true
        lassoState.error = ''
        renderLassoPanel()
        try {
          const response = await fetch(apiUrl('/api/pesquisa/mapa-indices-selecionados/export'), {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              ...(getAuthToken() ? { 'X-Auth-Token': getAuthToken() } : {}),
            },
            body: JSON.stringify({ modelos: lassoState.selectedModels }),
          })
          if (!response.ok) {
            const payloadResp = await readJsonSafely(response)
            throw new Error(String(payloadResp?.detail || 'Falha ao exportar seleção.'))
          }
          const blob = await response.blob()
          downloadBlob(blob, 'indices_mapa_pesquisa.xlsx')
        } catch (error) {
          lassoState.error = error?.message || 'Falha ao exportar seleção.'
        } finally {
          lassoState.downloading = false
          renderLassoPanel()
        }
      }

      map.on('mousedown', onLassoMouseDown)
      map.on('mousemove', onLassoMouseMove)
      map.on('mouseup', onLassoMouseUp)
      cleanupLasso = () => {
        map.off('mousedown', onLassoMouseDown)
        map.off('mousemove', onLassoMouseMove)
        map.off('mouseup', onLassoMouseUp)
        lassoLayerGroup.clearLayers()
      }

      const lassoControl = L.control({ position: 'topright' })
      lassoControl.onAdd = () => {
        lassoRoot = L.DomUtil.create('div', 'leaflet-control-measure mesa-leaflet-lasso leaflet-bar leaflet-control')
        lassoRoot.setAttribute('aria-haspopup', 'true')

        const toggle = L.DomUtil.create('a', 'leaflet-control-measure-toggle mesa-leaflet-lasso-toggle', lassoRoot)
        toggle.href = '#'
        toggle.title = 'Selecionar dados por laço'
        toggle.setAttribute('aria-label', 'Selecionar dados por laço')
        toggle.innerHTML = '<span class="mesa-sr-only">Selecionar dados por laço</span>'

        lassoInteraction = L.DomUtil.create('div', 'leaflet-control-measure-interaction mesa-leaflet-measure-interaction', lassoRoot)
        lassoInteraction.style.display = 'none'

        const title = L.DomUtil.create('h3', 'mesa-leaflet-measure-title', lassoInteraction)
        title.textContent = 'Selecionar por laço'

        lassoHint = L.DomUtil.create('p', 'mesa-leaflet-measure-hint', lassoInteraction)
        lassoResults = L.DomUtil.create('div', 'mesa-leaflet-measure-results', lassoInteraction)

        const actions = L.DomUtil.create('div', 'mesa-leaflet-measure-actions', lassoInteraction)
        lassoStartButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn is-primary', actions)
        lassoStartButton.type = 'button'
        lassoStartButton.textContent = 'Criar laço'

        lassoCancelButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn', actions)
        lassoCancelButton.type = 'button'
        lassoCancelButton.textContent = 'Cancelar'

        lassoDownloadButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn is-primary', actions)
        lassoDownloadButton.type = 'button'
        lassoDownloadButton.textContent = 'Baixar XLSX'

        lassoCloseButton = L.DomUtil.create('button', 'mesa-leaflet-measure-btn', actions)
        lassoCloseButton.type = 'button'
        lassoCloseButton.textContent = 'Fechar'

        L.DomEvent.disableClickPropagation(lassoRoot)
        L.DomEvent.disableScrollPropagation(lassoRoot)
        L.DomEvent.on(toggle, 'click', (event) => {
          L.DomEvent.stop(event)
          if (lassoState.active) return
          lassoState.panelOpen = !lassoState.panelOpen
          renderLassoPanel()
        })
        L.DomEvent.on(lassoStartButton, 'click', (event) => {
          L.DomEvent.stop(event)
          startLasso()
        })
        L.DomEvent.on(lassoCancelButton, 'click', (event) => {
          L.DomEvent.stop(event)
          resetLassoState()
        })
        L.DomEvent.on(lassoDownloadButton, 'click', (event) => {
          L.DomEvent.stop(event)
          void downloadLassoSelection()
        })
        L.DomEvent.on(lassoCloseButton, 'click', (event) => {
          L.DomEvent.stop(event)
          if (lassoState.active) return
          lassoState.panelOpen = false
          renderLassoPanel()
        })

        renderLassoPanel()
        return lassoRoot
      }
      lassoControl.addTo(map)
    }

    if (payload.controls?.fullscreen) {
      addFullscreenControl(map, onToggleFullscreen)
    }

    if (payload.legend?.title) {
      const legendControl = construirLegendaControl(payload.legend)
      if (legendControl) {
        legendControl.onAdd = () => {
          const div = L.DomUtil.create('div', 'mesa-leaflet-legend')
          div.innerHTML = buildLegendHtml(payload.legend)
          return div
        }
        legendControl.addTo(map)
      }
    }

    addNoticeControl(map, payload.notice)

    map.on('zoomend overlayadd overlayremove', applyResponsiveRadius)
    if (hoverHighlightMetas.length) {
      map.on('overlayadd overlayremove', () => {
        hoverHighlightMetas.forEach((meta) => {
          clearHoverHighlight(meta.groupKey)
        })
      })
    }
    applyResponsiveRadius()

    if (!fitMapToPayloadBounds(map, payload, 48)) {
      setRuntimeError('Falha ao montar mapa interativo.')
    }

    return () => {
      if (dependencyAwareOverlays.length) {
        map.off('overlayadd', refreshDependencyAwareOverlays)
        map.off('overlayremove', refreshDependencyAwareOverlays)
      }
      disposed = true
      restoreMapInteractions?.()
      cleanupLasso?.()
      if (popupLoaderRef.current === carregarPopupRegistro) {
        popupLoaderRef.current = null
      }
      mapContainer?.removeEventListener('click', handlePopupActionClick, true)
      groupSelectionMarkerRef.current = null
      if (mapRef.current === map) {
        mapRef.current = null
      }
      map.remove()
    }
  }, [payload, sessionId, onToggleFullscreen])

  return (
    <div className="map-frame leaflet-map-host">
      <div ref={hostRef} className="leaflet-map-canvas" />
      {runtimeError ? <div className="leaflet-map-runtime-error">{runtimeError}</div> : null}
      {groupSelection ? (
        <div className="leaflet-market-group-modal-backdrop" onClick={closeGroupSelection}>
          <div
            className="leaflet-market-group-modal"
            role="dialog"
            aria-modal="true"
            aria-label="Selecionar dado de mercado"
            onClick={(event) => event.stopPropagation()}
          >
            <div className="leaflet-market-group-modal-head">
              <div>
                <strong>{groupSelection.title}</strong>
                {!groupSelection.hideCount ? (
                  <span>{Number(groupSelection.count) || groupSelection.items?.length || 0} registros sobrepostos</span>
                ) : null}
              </div>
              <button type="button" onClick={closeGroupSelection} aria-label="Fechar">×</button>
            </div>
            <div className="leaflet-market-group-list">
              {(groupSelection.items || []).map((item, index) => {
                const disabled = !item?.popup_request
                return (
                  <button
                    key={`${item?.indice ?? index}-${index}`}
                    type="button"
                    className="leaflet-market-group-option"
                    onClick={() => selectGroupedMarketItem(item)}
                    disabled={disabled}
                  >
                    <span>{item?.label || `INDICE ${item?.indice ?? index + 1}`}</span>
                    {item?.value ? (
                      <small>
                        {item?.value_label ? `${item.value_label}: ` : ''}
                        {item.value}
                      </small>
                    ) : null}
                  </button>
                )
              })}
            </div>
          </div>
        </div>
      ) : null}
    </div>
  )
})

export default LeafletMapFrame
