<%# app/views/operacoes_dashboard/index.html.erb %>
<%# Dashboard de análise de entregas por operação (KPIs, insucessos, STS, mapa). %>

<% content_for :head do %>
  <meta name="turbo-cache-control" content="no-cache">

  <%# Flatpickr (date range) — CSS no <head> %>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/themes/dark.css">
  <%# Leaflet (mapa de calor) %>
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
  <style>
    .flatpickr-calendar { background: #1a1a1a; border: 1px solid rgba(255,255,255,.1); box-shadow: 0 10px 30px rgba(0,0,0,.5); }
    .flatpickr-months, .flatpickr-weekdays, .flatpickr-weekday { background: #1a1a1a; color: #9ca3af; }
    .flatpickr-current-month, .flatpickr-monthDropdown-months, .numInput { color: #fff; }
    .flatpickr-day { color: #e5e7eb; }
    .flatpickr-day:hover { background: #2a2a2a; border-color: #2a2a2a; }
    .flatpickr-day.today { border-color: #f97316; }
    .flatpickr-day.selected, .flatpickr-day.startRange, .flatpickr-day.endRange,
    .flatpickr-day.selected:hover, .flatpickr-day.startRange:hover, .flatpickr-day.endRange:hover {
      background: #f97316; border-color: #f97316; color: #0a0a0a;
    }
    .flatpickr-day.inRange { background: rgba(249,115,22,.18); border-color: rgba(249,115,22,.18); box-shadow: -5px 0 0 rgba(249,115,22,.18), 5px 0 0 rgba(249,115,22,.18); }
    .flatpickr-months .flatpickr-prev-month svg, .flatpickr-months .flatpickr-next-month svg { fill: #f97316; }
    /* O mapa do Leaflet usa fundo claro — moldura escura ao redor */
    .leaflet-container { background: #1a1a1a; }
    /* Modo escuro = OpenStreetMap completo (detalhe igual ao claro) invertido,
       para ruas e nomes ficarem bem visíveis sobre fundo escuro. */
    .tiles-escuro-contraste { filter: invert(1) hue-rotate(180deg) brightness(0.95) contrast(1.05); }
  </style>
<% end %>

<% hoje = Date.current %>
<% atalhos = {
     'Hoje'     => [hoje, hoje],
     '7 dias'   => [hoje - 6, hoje],
     'Este mês' => [hoje.beginning_of_month, hoje],
     '30 dias'  => [hoje - 29, hoje]
   } %>
<% ini_iso = @periodo_inicio.strftime('%Y-%m-%d') %>
<% fim_iso = @periodo_fim.strftime('%Y-%m-%d') %>
<%# Parâmetros de operação a preservar ao trocar de modo/período %>
<% ctx_ops = { operacao: @operacao, op_a: @op_a, op_b: @op_b }.compact %>

<%# Atualiza os dados sozinho a cada 5 min (data-auto-refresh-interval-value em
    ms), sem o operador apertar F5. Ver auto_refresh_controller.js. %>
<div class="space-y-6" data-controller="auto-refresh" data-auto-refresh-interval-value="300000">

  <%# ── Cabeçalho ─────────────────────────────────────────── %>
  <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
    <div>
      <h1 class="text-2xl font-bold text-white">Dashboard de Operações</h1>
      <p class="text-gray-400 text-sm mt-0.5">
        <% if @modo == 'global' %>
          <%= icone :global %> Global · <%= @periodo_inicio.strftime('%d/%m/%Y') %> – <%= @periodo_fim.strftime('%d/%m/%Y') %>
        <% elsif @modo == 'comparar' %>
          <%= icone :comparar %> Comparação entre operações (cada uma no seu próprio período)
        <% elsif @metricas&.data_inicio %>
          <%= @metricas.operacoes_label %> · <%= @metricas.data_inicio.strftime('%d/%m/%Y') %> – <%= @metricas.data_fim.strftime('%d/%m/%Y') %>
          (operação inteira)
          <%# O período escolhido NÃO recorta os KPIs desta tela (eles têm que
              bater com a planilha entregue ao cliente); ele aparece aqui como
              referência para comparar com o dashboard financeiro. %>
          <% if @periodo_explicito %>
            <span class="text-gray-500">·</span>
            <span class="text-gray-300">no período <%= @periodo_inicio.strftime('%d/%m') %>–<%= @periodo_fim.strftime('%d/%m/%Y') %>:
              <%= @metricas.total_no_periodo(@periodo_inicio, @periodo_fim) %> NFs</span>
          <% end %>
        <% elsif @operacao %>
          <%= Operacao.label(@operacao) %>
        <% end %>
        · Atualizado <%= ultima_atualizacao_label %>
      </p>
    </div>

    <%# Filtro de período — Global e Operação. No modo Operação ele só entra em
        vigor quando o operador ESCOLHE uma faixa (params inicio/fim): sem isso o
        recorte é a operação inteira, senão abrir uma operação de meses atrás
        cairia no mês corrente e mostraria zero. %>
    <% if %w[global operacao].include?(@modo) %>
      <div class="flex flex-wrap items-center gap-2">
        <div class="relative">
          <span class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2"><%= icone :calendario, espaco: false %></span>
          <input type="text" id="periodo-range" readonly placeholder="Selecione o período"
                 value="<%= @periodo_inicio.strftime('%d/%m/%Y') %> até <%= @periodo_fim.strftime('%d/%m/%Y') %>"
                 title="<%= @modo == 'operacao' ? 'No modo Operação o período não recorta os KPIs (eles seguem a operação inteira, como na planilha do cliente) — vale para as notas fora da operação e para comparar com o financeiro.' : 'Faixa de datas da visão Global.' %>"
                 class="cursor-pointer pl-4 pr-10 py-2.5 bg-[#1a1a1a] border border-white/10 rounded-xl
                        text-white text-sm w-full sm:w-[240px] focus:outline-none focus:border-orange-500 placeholder-gray-500">
        </div>
        <div class="flex flex-wrap items-center gap-1">
          <% atalhos.each do |label, (ini, fim)| %>
            <%# No Global a faixa SEMPRE vale (é o que define a visão); no modo
                Operação só quando escolhida — senão o atalho apareceria aceso
                sem estar sendo aplicado. %>
            <% ativo = (@periodo_explicito || @modo == 'global') && @periodo_inicio == ini && @periodo_fim == fim %>
            <%= link_to label,
                operacoes_dashboard_path(ctx_ops.merge(modo: @modo, inicio: ini.strftime('%Y-%m-%d'), fim: fim.strftime('%Y-%m-%d'))),
                data: { turbo: false },
                class: "px-3 py-2.5 rounded-xl text-sm whitespace-nowrap border #{ativo ? 'bg-orange-500 text-black border-orange-500 font-bold' : 'bg-[#1a1a1a] text-gray-300 border-white/10 hover:border-orange-500'}" %>
          <% end %>
          <%# Limpa a faixa de datas da URL (os KPIs já são sempre da operação
              inteira; sem período a tela também some com o painel de notas
              fora da operação). %>
          <% if @modo == 'operacao' %>
            <%# Rótulo é "Limpar período" e não "Operação inteira": o total já é
                sempre o da operação inteira, o que este botão faz é tirar a
                faixa de datas da URL. %>
            <%= link_to 'Limpar período',
                operacoes_dashboard_path(ctx_ops.merge(modo: 'operacao')),
                data: { turbo: false },
                class: "px-3 py-2.5 rounded-xl text-sm whitespace-nowrap border #{@periodo_explicito ? 'bg-[#1a1a1a] text-gray-300 border-white/10 hover:border-orange-500' : 'bg-orange-500 text-black border-orange-500 font-bold'}" %>
          <% end %>
        </div>
      </div>
    <% end %>
  </div>

  <%# ── Abas de modo + atalho para a planilha ─────────────── %>
  <% modos = { 'operacao' => [:operacao, 'Operação'], 'global' => [:global, 'Global'], 'comparar' => [:comparar, 'Comparar'] } %>
  <%# A faixa de datas só viaja entre as abas se o operador tiver ESCOLHIDO uma.
      Carregar o default (mês corrente) ao entrar em "Operação" faria uma operação
      de meses atrás abrir zerada. %>
  <% ctx_periodo = @periodo_explicito ? { inicio: ini_iso, fim: fim_iso } : {} %>
  <div class="flex flex-wrap items-center justify-between gap-2 border-b border-white/5 pb-3">
    <div class="flex flex-wrap items-center gap-2">
      <% modos.each do |m, (ic, label)| %>
        <% ativo = @modo == m %>
        <%= link_to rotulo(ic, label, cor: (ativo ? nil : 'text-brand-laranja')),
            operacoes_dashboard_path(ctx_ops.merge(ctx_periodo).merge(modo: m)),
            data: { turbo: false },
            class: "px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap #{ativo ? 'bg-orange-500 text-black' : 'bg-[#1a1a1a] text-gray-300 border border-white/10 hover:border-orange-500'}" %>
      <% end %>
    </div>

    <%# Planilha da operação (tabela espelho) — leva o contexto atual junto %>
    <% planilha_ctx = request.query_parameters
         .slice('f_driver', 'f_sts', 'f_status', 'f_obs', 'f_resultado', 'f_data')
         .merge(modo: (@modo == 'global' ? 'global' : 'operacao'), inicio: ini_iso, fim: fim_iso) %>
    <% planilha_ctx[:operacao] = @operacao if @operacao %>
    <%= link_to rotulo(:planilha, 'Planilha da operação', cor: nil), operacoes_planilha_path(planilha_ctx),
        data: { turbo: false },
        class: 'px-4 py-2 rounded-xl text-sm font-semibold whitespace-nowrap bg-[#1a1a1a]
                text-orange-400 border border-orange-500/40 hover:bg-orange-500 hover:text-black' %>
  </div>

  <% if @tabelas_validas.blank? %>
    <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-10 text-center text-gray-400">
      Nenhuma operação encontrada no banco (tabelas <code class="text-orange-400">gade_entregas_*</code>).
    </div>
  <% else %>

    <%# ── Filtros ativos (cross-filter por clique nas tabelas) ── %>
    <% if @chips.any? && %w[operacao global].include?(@modo) %>
      <div class="flex flex-wrap items-center gap-2">
        <span class="text-xs text-gray-500 uppercase tracking-wider">Filtros ativos:</span>
        <% @chips.each do |chip| %>
          <span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-orange-500/15 border border-orange-500/30 text-orange-300 text-xs">
            <%= chip[:rotulo] %>: <strong class="text-white"><%= chip[:display] || chip[:valor] %></strong>
            <%= link_to icone(:fechar, cor: nil, tamanho: 'w-3.5 h-3.5'), operacoes_dashboard_path(request.query_parameters.except(chip[:param])),
                  data: { turbo: false }, class: 'hover:text-white font-bold', title: 'Remover filtro' %>
          </span>
        <% end %>
        <%= link_to 'Limpar tudo',
              operacoes_dashboard_path(request.query_parameters.except(*@chips.map { |c| c[:param] })),
              data: { turbo: false }, class: 'text-xs text-gray-400 hover:text-orange-400 underline' %>
      </div>
    <% end %>

    <%# ── Seletor de operação por modo ───────────────────── %>
    <% if @modo == 'operacao' %>
      <form method="get" action="<%= operacoes_dashboard_path %>" data-turbo="false" class="flex flex-wrap items-center gap-2">
        <input type="hidden" name="modo" value="operacao">
        <input type="hidden" name="inicio" value="<%= ini_iso %>">
        <input type="hidden" name="fim"    value="<%= fim_iso %>">
        <label class="text-gray-400 text-sm">Operação:</label>
        <select name="operacao" class="auto-submit bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500 min-w-[240px]">
          <% @operacoes_agrupadas.each do |titulo, ops| %>
            <optgroup label="<%= titulo %>">
              <% ops.each do |op| %>
                <option value="<%= op[:tabela] %>" <%= 'selected' if op[:tabela] == @operacao %>><%= op[:label] %></option>
              <% end %>
            </optgroup>
          <% end %>
        </select>
      </form>

      <% if @metricas %>
        <%= render 'painel', metricas: @metricas, filtravel: true %>
        <%= render 'mapa', metricas: @metricas %>
      <% end %>
      <% if @fora_operacao %>
        <%= render 'fora_operacao', fora: @fora_operacao, inicio: @periodo_inicio, fim: @periodo_fim %>
      <% end %>

    <% elsif @modo == 'global' %>
      <div class="bg-[#1a1a1a] rounded-2xl border border-white/5 p-4 text-sm text-gray-300">
        <%= icone :global %> Visão <strong class="text-white">Global</strong> — todas as
        <%= @tabelas_validas.size %> operações agregadas no período.
      </div>
      <% if @metricas %>
        <%= render 'painel', metricas: @metricas, filtravel: true %>
        <%= render 'mapa', metricas: @metricas %>
      <% end %>
      <% if @fora_operacao %>
        <%= render 'fora_operacao', fora: @fora_operacao, inicio: @periodo_inicio, fim: @periodo_fim %>
      <% end %>

    <% else # comparar %>
      <form method="get" action="<%= operacoes_dashboard_path %>" data-turbo="false" class="grid grid-cols-1 sm:grid-cols-2 gap-4">
        <input type="hidden" name="modo" value="comparar">
        <input type="hidden" name="inicio" value="<%= ini_iso %>">
        <input type="hidden" name="fim"    value="<%= fim_iso %>">
        <div class="flex items-center gap-2">
          <label class="text-orange-400 text-sm font-semibold">A:</label>
          <select name="op_a" class="auto-submit flex-1 bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-orange-500">
            <% @operacoes_agrupadas.each do |titulo, ops| %>
              <optgroup label="<%= titulo %>">
                <% ops.each do |op| %>
                  <option value="<%= op[:tabela] %>" <%= 'selected' if op[:tabela] == @op_a %>><%= op[:label] %></option>
                <% end %>
              </optgroup>
            <% end %>
          </select>
        </div>
        <div class="flex items-center gap-2">
          <label class="text-blue-400 text-sm font-semibold">B:</label>
          <select name="op_b" class="auto-submit flex-1 bg-[#1a1a1a] border border-white/10 rounded-xl text-white text-sm px-3 py-2.5 focus:outline-none focus:border-blue-500">
            <% @operacoes_agrupadas.each do |titulo, ops| %>
              <optgroup label="<%= titulo %>">
                <% ops.each do |op| %>
                  <option value="<%= op[:tabela] %>" <%= 'selected' if op[:tabela] == @op_b %>><%= op[:label] %></option>
                <% end %>
              </optgroup>
            <% end %>
          </select>
        </div>
      </form>

      <%# Comparativo direto A x B (tabela + barras agrupadas) %>
      <% if @metricas_a && @metricas_b %>
        <%= render 'comparativo', a: @metricas_a, b: @metricas_b,
              label_a: @metricas_a.operacoes_label, label_b: @metricas_b.operacoes_label %>
      <% end %>

      <%# Detalhe completo de cada operação (recolhível) %>
      <details class="bg-[#1a1a1a] rounded-2xl border border-white/5">
        <summary class="cursor-pointer px-6 py-4 text-sm text-gray-300 hover:text-white select-none">
          <%= icone :buscar %> Ver detalhe completo de cada operação
        </summary>
        <div class="grid grid-cols-1 xl:grid-cols-2 gap-6 p-4 pt-0">
          <div>
            <% if @metricas_a %>
              <%= render 'painel', metricas: @metricas_a, titulo: "A · #{@metricas_a.operacoes_label}" %>
            <% else %>
              <p class="text-gray-500 text-sm">Selecione a operação A.</p>
            <% end %>
          </div>
          <div>
            <% if @metricas_b %>
              <%= render 'painel', metricas: @metricas_b, titulo: "B · #{@metricas_b.operacoes_label}" %>
            <% else %>
              <p class="text-gray-500 text-sm">Selecione a operação B.</p>
            <% end %>
          </div>
        </div>
      </details>
    <% end %>
  <% end %>
</div>

<%# ── Scripts: flatpickr, Chart.js, Leaflet ───────────────── %>
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/flatpickr.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr@4.6.13/dist/l10n/pt.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js"></script>

<script>
  (function () {
    const ISO = '<%= ini_iso %>';
    const ISO_FIM = '<%= fim_iso %>';

    // ── Flatpickr (período) ──
    function initPeriodo() {
      const input = document.getElementById('periodo-range');
      if (!input || typeof flatpickr === 'undefined' || input._flatpickr) return;
      const toISO = (d) =>
        d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
      const parseISO = (s) => { const [y, m, d] = s.split('-').map(Number); return new Date(y, m - 1, d); };
      flatpickr(input, {
        mode: 'range',
        locale: Object.assign({}, flatpickr.l10ns.pt, { rangeSeparator: ' até ' }),
        dateFormat: 'd/m/Y',
        defaultDate: [parseISO(ISO), parseISO(ISO_FIM)],
        maxDate: 'today',
        onClose: function (selectedDates) {
          if (selectedDates.length === 0) return;
          const ini = toISO(selectedDates[0]);
          const fim = toISO(selectedDates[selectedDates.length - 1]);
          const url = new URL(window.location.href);
          url.searchParams.set('inicio', ini);
          url.searchParams.set('fim', fim);
          window.location.href = url.toString();
        }
      });
    }

    // ── Selects que submetem o form ao mudar ──
    function initAutoSubmit() {
      document.querySelectorAll('select.auto-submit').forEach(function (s) {
        if (s.dataset.bound) return;
        s.dataset.bound = '1';
        s.addEventListener('change', function () { s.form.requestSubmit(); });
      });
    }

    // ── Cores da marca (laranja + azul p/ falha) ──
    const COR_OK = '#f97316';   // laranja Reem (completas)
    const COR_FAIL = '#3b82f6'; // azul (falhas), igual ao card de Recusas
    const COR_A = '#f97316';    // comparativo: A = laranja da marca
    const COR_B = '#3b82f6';    // comparativo: B = azul (contraste)

    // Cross-filter por clique nos gráficos: recarrega a página com o filtro,
    // preservando os demais parâmetros (mesma lógica dos links das tabelas).
    function aplicarFiltroDash(novos) {
      const url = new URL(window.location.href);
      Object.keys(novos).forEach(function (k) {
        const v = novos[k];
        if (v === null || v === '') url.searchParams.delete(k);
        else url.searchParams.set(k, v);
      });
      window.location.assign(url.toString());
    }
    function cursorPonteiro(e, els) { e.native.target.style.cursor = els.length ? 'pointer' : 'default'; }

    // ── Charts (donut + barras + comparativo) ──
    function initCharts() {
      if (typeof Chart === 'undefined') return;

      document.querySelectorAll('canvas.dash-donut').forEach(function (ctx) {
        Chart.getChart(ctx)?.destroy();
        new Chart(ctx, {
          type: 'doughnut',
          data: {
            labels: ['Completas', 'Falhas'],
            datasets: [{
              data: [Number(ctx.dataset.completed), Number(ctx.dataset.failed)],
              backgroundColor: [COR_OK, COR_FAIL],
              borderColor: '#1a1a1a', borderWidth: 2
            }]
          },
          options: {
            responsive: true, maintainAspectRatio: false, cutout: '62%',
            plugins: { legend: { display: false } },
            onHover: cursorPonteiro,
            onClick: function (e, els) {
              if (!els.length) return;
              aplicarFiltroDash({ f_resultado: els[0].index === 0 ? 'completed' : 'failed' });
            }
          }
        });
      });

      // Plugin: desenha o valor centralizado DENTRO de cada segmento da barra
      // (pula segmentos baixos demais e valores zero).
      const barValueLabels = {
        id: 'barValueLabels',
        afterDatasetsDraw: function (chart) {
          const g = chart.ctx;
          g.save();
          g.font = '600 11px system-ui, -apple-system, sans-serif';
          g.fillStyle = '#ffffff';
          g.textAlign = 'center';
          g.textBaseline = 'middle';
          chart.data.datasets.forEach(function (ds, i) {
            const meta = chart.getDatasetMeta(i);
            if (meta.hidden) return;
            meta.data.forEach(function (bar, idx) {
              const v = Number(ds.data[idx]) || 0;
              if (!v) return;
              const p = bar.getProps(['x', 'y', 'base'], true);
              if (Math.abs(p.base - p.y) < 16) return; // segmento pequeno demais
              g.fillText(String(v), p.x, (p.y + p.base) / 2);
            });
          });
          g.restore();
        }
      };

      // Total do dia (soma das séries) para o % no tooltip.
      const totalDoDia = function (datasets, idx) {
        return datasets.reduce(function (s, d) { return s + (Number(d.data[idx]) || 0); }, 0);
      };

      document.querySelectorAll('canvas.dash-bars').forEach(function (ctx) {
        Chart.getChart(ctx)?.destroy();
        new Chart(ctx, {
          type: 'bar',
          data: {
            labels: JSON.parse(ctx.dataset.labels),
            datasets: [
              { label: 'Completas', data: JSON.parse(ctx.dataset.completed), backgroundColor: COR_OK },
              { label: 'Falhas',    data: JSON.parse(ctx.dataset.failed),    backgroundColor: COR_FAIL }
            ]
          },
          plugins: [barValueLabels],
          options: {
            responsive: true, maintainAspectRatio: false,
            interaction: { mode: 'index', intersect: false },
            onHover: cursorPonteiro,
            onClick: function (e, els) {
              if (!els.length) return;
              const el = els[0];
              const iso = String(this.data.labels[el.index]).split('/').reverse().join('-');
              aplicarFiltroDash({ f_data: iso, f_resultado: el.datasetIndex === 0 ? 'completed' : 'failed' });
            },
            scales: {
              x: { stacked: true, ticks: { color: '#6b7280', maxTicksLimit: 12 }, grid: { color: 'rgba(255,255,255,0.04)' } },
              y: { stacked: true, beginAtZero: true, ticks: { color: '#6b7280' }, grid: { color: 'rgba(255,255,255,0.04)' } }
            },
            plugins: {
              legend: { labels: { color: '#9ca3af' } },
              // Card de hover no tema escuro do app: data + Completas/Falhas (n e %) + Total.
              tooltip: {
                mode: 'index', intersect: false,
                backgroundColor: '#1f1f1f',
                borderColor: 'rgba(255,255,255,0.1)', borderWidth: 1,
                titleColor: '#ffffff', bodyColor: '#d1d5db', footerColor: '#9ca3af',
                padding: 12, cornerRadius: 12, boxPadding: 4, usePointStyle: true,
                titleFont: { weight: '700' }, footerFont: { weight: '600' },
                callbacks: {
                  title: function (items) { return items.length ? items[0].label : ''; },
                  label: function (item) {
                    const total = totalDoDia(item.chart.data.datasets, item.dataIndex);
                    const v = Number(item.raw) || 0;
                    const pct = total ? (v * 100 / total).toFixed(2) : '0.00';
                    return '  ' + item.dataset.label + ': ' + v + '  (' + pct + '%)';
                  },
                  footer: function (items) {
                    if (!items.length) return '';
                    const total = totalDoDia(items[0].chart.data.datasets, items[0].dataIndex);
                    return 'Total: ' + total + '  (100%)';
                  }
                }
              }
            }
          }
        });
      });

      // Comparativo A x B (barras agrupadas)
      document.querySelectorAll('canvas.cmp-bars').forEach(function (ctx) {
        Chart.getChart(ctx)?.destroy();
        new Chart(ctx, {
          type: 'bar',
          data: {
            labels: ['Total', 'Sucesso', 'Recusas', 'Pendentes'],
            datasets: [
              { label: 'A · ' + (ctx.dataset.labelA || 'A'), data: JSON.parse(ctx.dataset.a), backgroundColor: COR_A },
              { label: 'B · ' + (ctx.dataset.labelB || 'B'), data: JSON.parse(ctx.dataset.b), backgroundColor: COR_B }
            ]
          },
          options: {
            responsive: true, maintainAspectRatio: false,
            scales: {
              x: { ticks: { color: '#6b7280' }, grid: { color: 'rgba(255,255,255,0.04)' } },
              y: { beginAtZero: true, ticks: { color: '#6b7280' }, grid: { color: 'rgba(255,255,255,0.04)' } }
            },
            plugins: { legend: { labels: { color: '#9ca3af' } } }
          }
        });
      });
    }

    // ── Mapa de calor (Leaflet) ──
    function escapeHtml(s) {
      return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
        return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
      });
    }

    // Ícone do sprite (public/icons.svg) — mesmos SVG usados no resto do sistema.
    function ico(id, cor) {
      return '<svg width="12" height="12" fill="' + (cor || '#f97316') + '" style="vertical-align:-1px">' +
             '<use href="/icons.svg#' + id + '"></use></svg>';
    }

    function popupHtml(p) {
      const linha = (icone, val) => val ? ('<div>' + icone + ' ' + escapeHtml(val) + '</div>') : '';
      const foto = p.foto
        ? '<a href="' + escapeHtml(p.foto) + '" target="_blank" rel="noopener">' +
          '<img src="' + escapeHtml(p.foto) + '" alt="Fachada" loading="lazy" ' +
          'onerror="this.style.display=\'none\';this.parentNode.nextElementSibling.style.display=\'block\'" ' +
          'style="margin-top:8px;width:100%;max-height:160px;object-fit:cover;border-radius:6px;display:block"></a>' +
          '<div style="display:none;margin-top:6px;color:#9ca3af;font-size:11px">' + ico('camera-fill', '#9ca3af') + ' Foto indisponível (link expirado/sem acesso)</div>'
        : '<div style="margin-top:6px;color:#6b7280;font-size:11px">' + ico('camera-fill', '#6b7280') + ' Sem foto da fachada para esta entrega</div>';
      const statusLinha = p.sucesso
        ? '<div style="color:#16a34a;font-size:11px;margin-bottom:6px">' + ico('check-lg', '#16a34a') + ' Entrega realizada</div>'
        : '<div style="color:#3b82f6;font-size:11px;margin-bottom:6px">' + ico('x-lg', '#3b82f6') + ' Insucesso' +
          (p.motivo ? ' — ' + escapeHtml(p.motivo) : '') + '</div>';
      return '' +
        '<div style="min-width:210px;max-width:240px">' +
        '<div style="font-weight:700;color:#f97316">NF ' + escapeHtml(p.nf) + '</div>' +
        '<div style="font-weight:600;margin-bottom:4px">' + escapeHtml(p.nome) + '</div>' +
        statusLinha +
        linha(ico('geo-alt-fill'), p.endereco) +
        linha(ico('hospital-fill'), p.unidade) +
        linha(ico('truck'), p.motorista) +
        linha(ico('car-front-fill'), p.veiculo) +
        linha(ico('clock-fill'), p.checkout) +
        foto +
        '</div>';
    }

    // Ícone de localização (balão) — sem depender de imagem externa.
    // Cor por resultado: laranja = sucesso, azul = insucesso (falha/óbito).
    function pinIcon(cor) {
      return L.divIcon({
        className: 'pin-entrega',
        html: '<svg width="28" height="40" viewBox="0 0 28 40" xmlns="http://www.w3.org/2000/svg">' +
              '<path d="M14 0C6.8 0 1 5.8 1 13c0 9.7 13 27 13 27s13-17.3 13-27C27 5.8 21.2 0 14 0z" ' +
              'fill="' + cor + '" stroke="#0a0a0a" stroke-width="1.5"/>' +
              '<circle cx="14" cy="13" r="5" fill="#0a0a0a"/></svg>',
        iconSize: [28, 40],
        iconAnchor: [14, 40],
        popupAnchor: [0, -36]
      });
    }

    function initMapas() {
      if (typeof L === 'undefined') return;
      const ICONE_OK    = pinIcon('#f97316'); // sucesso — laranja
      const ICONE_FALHA = pinIcon('#3b82f6'); // insucesso — azul
      document.querySelectorAll('.mapa-box').forEach(function (box) {
        const el = box.querySelector('.mapa-calor');
        if (!el || el._leaflet_id) return;

        const heat    = JSON.parse(el.dataset.heat || '[]');
        const pontos  = JSON.parse(el.dataset.pontos || '[]');
        const centro  = JSON.parse(el.dataset.centro || '[-23.55,-46.63]');

        // Camadas base (todas grátis, sem chave): escuro (padrão, combina com o
        // tema), claro e satélite. O escuro usa o OpenStreetMap COMPLETO (mesmo
        // detalhe do claro: ruas, nomes, POIs) e é escurecido por filtro CSS de
        // inversão (.tiles-escuro-contraste).
        const escuro = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
          attribution: '© OpenStreetMap', maxZoom: 19,
          className: 'tiles-escuro-contraste'
        });
        const claro = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
          attribution: '© OpenStreetMap', maxZoom: 19
        });
        const satelite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
          attribution: 'Tiles © Esri', maxZoom: 19
        });

        const mapa = L.map(el, { layers: [escuro] }).setView(centro, 11);
        L.control.layers(
          { [ico('moon-fill') + ' Escuro']: escuro,
            [ico('map-fill') + ' Claro']: claro,
            [ico('broadcast-pin') + ' Satélite']: satelite },
          {}, { position: 'topright', collapsed: true }
        ).addTo(mapa);

        // Camada de calor (leve — entra por padrão)
        const heatLayer = (heat.length && L.heatLayer)
          ? L.heatLayer(heat, { radius: 18, blur: 22, maxZoom: 14 }) : null;

        // Camada de pontos: criada SOB DEMANDA (só na 1ª vez que abrir "Pontos").
        // O popup é uma FUNÇÃO, então o HTML da foto só é montado/baixado quando
        // o balão é clicado — nada de imagem é requisitado ao carregar o mapa.
        let marcadores = null;
        let entradas = [];               // [{ marker, p }] — para a busca filtrar sem recriar
        const resultadosBusca = L.layerGroup();
        function construirMarcadores() {
          if (marcadores) return marcadores;
          marcadores = L.layerGroup();
          pontos.forEach(function (p) {
            const marker = L.marker([p.lat, p.lng], { icon: p.sucesso ? ICONE_OK : ICONE_FALHA })
              .bindPopup(function () { return popupHtml(p); }, { maxWidth: 260 });
            marker.addTo(marcadores);
            entradas.push({ marker: marker, p: p });
          });
          return marcadores;
        }

        // Começa no Calor (cai para Pontos se não houver calor)
        let vistaAtual = heatLayer ? 'heat' : 'pontos';
        (heatLayer || construirMarcadores()).addTo(mapa);

        function mostrar(view) {
          vistaAtual = view;
          if (heatLayer)   mapa.removeLayer(heatLayer);
          if (marcadores)  mapa.removeLayer(marcadores);
          if (view === 'pontos') { construirMarcadores().addTo(mapa); }
          else { (heatLayer || construirMarcadores()).addTo(mapa); }
          box.querySelectorAll('.map-toggle button').forEach(function (b) {
            const on = b.dataset.view === view;
            b.classList.toggle('bg-orange-500', on);
            b.classList.toggle('text-black', on);
            b.classList.toggle('font-semibold', on);
            b.classList.toggle('text-gray-300', !on);
          });
        }

        box.querySelectorAll('.map-toggle button').forEach(function (b) {
          b.addEventListener('click', function () {
            const inputBusca = box.querySelector('.mapa-busca');
            if (inputBusca && inputBusca.value) { inputBusca.value = ''; }
            limparBusca();
            mostrar(b.dataset.view);
          });
        });

        // ── Busca por NF / paciente / veículo ──
        const inputBusca = box.querySelector('.mapa-busca');
        const infoBusca  = box.querySelector('.mapa-busca-info');

        function limparBusca() {
          mapa.removeLayer(resultadosBusca);
          resultadosBusca.clearLayers();
          if (infoBusca) infoBusca.textContent = '';
        }

        function aplicarBusca(termo) {
          const q = String(termo || '').trim().toLowerCase();
          if (!q) {
            limparBusca();
            mostrar(vistaAtual);  // restaura a visão padrão (Calor ou Pontos)
            return;
          }
          construirMarcadores();
          if (heatLayer)  mapa.removeLayer(heatLayer);
          if (marcadores) mapa.removeLayer(marcadores);
          resultadosBusca.clearLayers();

          const casa = function (v) { return String(v == null ? '' : v).toLowerCase().indexOf(q) !== -1; };
          const achados = entradas.filter(function (e) {
            return casa(e.p.nf) || casa(e.p.nome) || casa(e.p.veiculo);
          });
          achados.forEach(function (e) { e.marker.addTo(resultadosBusca); });
          resultadosBusca.addTo(mapa);

          if (achados.length) {
            const grupo = L.featureGroup(achados.map(function (e) { return e.marker; }));
            mapa.fitBounds(grupo.getBounds(), { padding: [40, 40], maxZoom: 16 });
            if (achados.length === 1) { achados[0].marker.openPopup(); }
            if (infoBusca) infoBusca.textContent = achados.length + (achados.length === 1 ? ' resultado' : ' resultados');
          } else {
            if (infoBusca) infoBusca.textContent = 'Nenhum resultado';
          }
        }

        if (inputBusca) {
          let debounce = null;
          inputBusca.addEventListener('input', function () {
            clearTimeout(debounce);
            const valor = inputBusca.value;
            debounce = setTimeout(function () { aplicarBusca(valor); }, 200);
          });
        }

        // Tela cheia (Fullscreen API sobre o container do mapa)
        const btnFs = box.querySelector('.map-fullscreen');
        if (btnFs) {
          btnFs.addEventListener('click', function () {
            if (document.fullscreenElement) { document.exitFullscreen(); }
            else if (el.requestFullscreen) { el.requestFullscreen(); }
          });
        }
        document.addEventListener('fullscreenchange', function () {
          setTimeout(function () { mapa.invalidateSize(); }, 150);
        });

        setTimeout(function () { mapa.invalidateSize(); }, 200);
      });
    }

    function initTudo() { initPeriodo(); initAutoSubmit(); initCharts(); initMapas(); }
    initTudo();
    document.addEventListener('turbo:load', initTudo);
  })();
</script>
