// assets/js/dashboard.js - Dashboard ULTRA RÁPIDO cada 500ms
(function () {
  'use strict';

  const cfg = window.__FP_CONFIG || {};
  const api = cfg.api || 'api.php';
  const pumpSvg = cfg.pumpSvg || '';

  /* ---------- Helpers ---------- */
  function esc(s) {
    return String(s || '').replace(/[&<>"']/g, function (m) {
      return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[m]);
    });
  }

  function fmtCurrency(v) { return '$' + (parseFloat(v || 0).toFixed(2)); }

  function fmtDateTime(dtStr) {
    if (!dtStr) return ['', ''];
    try {
      const normalized = String(dtStr).replace(' ', 'T');
      const d = new Date(normalized);
      if (isNaN(d)) {
        const parts = String(dtStr).split(' ');
        return [parts[0] || dtStr, parts[1] || ''];
      }
      return [d.toLocaleDateString(), d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })];
    } catch (e) {
      const parts = String(dtStr).split(' ');
      return [parts[0] || dtStr, parts[1] || ''];
    }
  }

  async function fetchJson(url) {
    const sep = url.indexOf('?') === -1 ? '?' : '&';
    const timeUrl = url + sep + 'nocache=' + Date.now();
    const r = await fetch(timeUrl, { 
      method: 'GET',
      cache: 'no-store',
      headers: {
        'Cache-Control': 'no-cache',
        'Pragma': 'no-cache'
      }
    });
    if (!r.ok) {
      throw new Error('HTTP ' + r.status);
    }
    return await r.json();
  }

  function ensureChartLoaded() {
    return new Promise((resolve, reject) => {
      if (typeof window.Chart !== 'undefined') return resolve();
      const src = 'https://cdn.jsdelivr.net/npm/chart.js@4.3.0/dist/chart.umd.min.js';
      const s = document.createElement('script');
      s.src = src;
      s.async = true;
      s.onload = () => { if (typeof window.Chart !== 'undefined') resolve(); else reject(new Error('Chart loaded but Chart is undefined')); };
      s.onerror = (e) => reject(new Error('Failed to load Chart.js: ' + (e && e.message ? e.message : e)));
      document.head.appendChild(s);
    });
  }

  /* ---------- DOM refs ---------- */
  let priceTilesEl, pumpsGrid, psModal, psModalBody, psModalTitle, psModalSubtitle, psModalContent;
  let psModalClose, psModalClose2, psSearch, psExpandAll, psCollapseAll;

  /* ---------- Station cache ---------- */
  let stationInfo = null;
  let _stationPromise = null;
  let _warnedRecentListMissing = false;

  async function getStation(force = false) {
    if (!force && stationInfo) return stationInfo;
    if (!force && _stationPromise) return _stationPromise;
    _stationPromise = (async () => {
      try {
        const res = await fetchJson(api + '?action=get_station');
        if (res && res.ok && res.station) { stationInfo = res.station; return stationInfo; }
        if (res && res.station) { stationInfo = res.station; return stationInfo; }
        if (res && Array.isArray(res) && res.length) { stationInfo = res[0]; return stationInfo; }
        stationInfo = null;
        return null;
      } catch (e) {
        console.warn('getStation error', e);
        stationInfo = null;
        return null;
      } finally {
        _stationPromise = null;
      }
    })();
    return _stationPromise;
  }

  /* ---------- Charts state ---------- */
  let productSalesChart = null;
  let dailySalesChart = null;
  const PALETTE = ['#ff2e00', '#000000', '#7f7f83', '#2a7a4b', '#6c757d', '#0d6efd', '#fd7e14', '#6f42c1', '#0dcaf0'];
  function paletteColor(i) { return PALETTE[i % PALETTE.length]; }
  function hexToRgba(hex, alpha) {
    if (!hex) return 'rgba(13,110,253,' + alpha + ')';
    if (hex.indexOf('gradient') !== -1) return 'rgba(13,110,253,' + alpha + ')';
    const h = hex.replace('#', '');
    const full = (h.length === 3) ? h.split('').map(c => c + c).join('') : h;
    const bigint = parseInt(full, 16);
    const r = (bigint >> 16) & 255, g = (bigint >> 8) & 255, b = bigint & 255;
    return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
  }

  /* ---------- Pump image by status ---------- */
  function getPumpImageByStatus(status, defaultImg) {
    const st = String(status || '').toLowerCase();
    const baseUrl = window.__FP_CONFIG.imagenFolder || 'imagen/';
    if (st === 'error' || st === 'closed') return baseUrl + 'surtidor_rojo.jpg';
    if (st === 'calling' || st === 'starting') return baseUrl + 'surtidor_naranja.jpg';
    if (st === 'fuelling' || st === 'fuel') return baseUrl + 'surtidor_azul.jpg';
    if (st === 'idle' || st === 'idell' || st === '') return baseUrl + 'surtidor_300x300.jpg';
    return defaultImg || baseUrl + 'surtidor_300x300.jpg';
  }

  /* ---------- UI templates ---------- */
  function productTileHtml(product, idx) {
    const paletteSet = [
      'linear-gradient(135deg,#0d6efd,#0056d6)',
      'linear-gradient(135deg,#6f42c1,#5a2ea6)',
      'linear-gradient(135deg,#0dcaf0,#0aa5d7)',
      'linear-gradient(135deg,#20c997,#17a589)',
      'linear-gradient(135deg,#fd7e14,#e56a00)'
    ];
    const bg = (product.bg_color && product.bg_color.trim()) ? product.bg_color : paletteSet[idx % paletteSet.length];
    let out = '<div class="col-12 col-md-6 col-lg-3">';
    out += '<div class="price-card" style="background:' + bg + '; color:#fff;">';
    out += '<div><div class="price-amount" style="color:#fff;">$' + (parseFloat(product.price || 0).toFixed(2)) + '</div>';
    out += '<div class="price-name" style="color:rgba(255,255,255,0.95)">' + esc(product.name) + '</div></div>';
    out += '<div class="d-flex justify-content-between align-items-center mt-2">';
    out += '<div class="price-sub" style="color:rgba(255,255,255,0.9)">SKU: ' + esc(product.sku || '') + '</div>';
    out += '<div style="font-size:12px;opacity:.9;color:rgba(255,255,255,0.9)">' + esc(product.unit || '') + '</div>';
    out += '</div></div></div>';
    return out;
  }

  function pumpCardHtml(p) {
    const status = (p.status || 'IDLE').toString();
    const img = getPumpImageByStatus(status, p.image_url || pumpSvg);
    const lastAmt = p.last_sale ? ('$' + parseFloat(p.last_sale.total).toFixed(2)) : '—';
    const lastDate = p.last_sale ? new Date(p.last_sale.created_at) : null;
    const dt = lastDate ? new Intl.DateTimeFormat(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }).format(lastDate) : '';
    const lastSaleId = p.last_sale ? p.last_sale.id : '';
    let out = '<div class="pump-card" data-pump-id="' + p.id + '" data-last-sale-id="' + lastSaleId + '" data-status="' + esc(status) + '">';
    out += '<div class="pump-image"><img src="' + esc(img) + '" alt="' + esc(p.name) + '"></div>';
    out += '<div class="pump-meta"><div class="pump-name">' + esc(p.name) + '</div>';
    out += '<div class="pump-last">' + lastAmt + '</div>';
    out += '<div class="pump-datetime">' + esc(dt) + '</div>';
    out += '<div class="pump-status" style="font-size:12px;color:#777;margin-top:2px">' + esc(status) + '</div>';
    out += '</div></div>';
    return out;
  }

  /* ---------- Charts ---------- */
  async function renderProductSalesChart(products, productsMap) {
    await ensureChartLoaded();
    const labels = (products || []).map(p => p.name);
    const data = (products || []).map(p => parseFloat(p.total) || 0);
    const bgColors = (products || []).map((p, i) => {
      const prod = productsMap && productsMap[p.id];
      return (prod && prod.bg_color && prod.bg_color.trim()) ? prod.bg_color : paletteColor(i);
    });
    const borderColors = bgColors.map(c => hexToRgba(c, 1));
    const canvas = document.getElementById('productSalesChart');
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    try { if (productSalesChart) productSalesChart.destroy(); } catch (e) { }
    productSalesChart = new Chart(ctx, {
      type: 'bar',
      data: { labels: labels, datasets: [{ label: 'Ventas ($)', data: data, backgroundColor: bgColors, borderColor: borderColors, borderWidth: 1, borderRadius: 8 }] },
      options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { display: false } }, y: { beginAtZero: true } } }
    });
  }

  async function renderDailySalesChart(daily, productsMap) {
    await ensureChartLoaded();
    if (!daily || !Array.isArray(daily.labels) || !Array.isArray(daily.series) || daily.series.length === 0) {
      try { if (dailySalesChart) dailySalesChart.destroy(); } catch (e) { }
      dailySalesChart = null;
      return;
    }
    const labels = daily.labels;
    const datasets = daily.series.map((s, idx) => {
      const prod = productsMap && productsMap[s.product_id];
      const color = (prod && prod.bg_color && prod.bg_color.trim()) ? prod.bg_color : paletteColor(idx);
      return {
        label: s.name,
        data: s.data.map(v => parseFloat(v) || 0),
        borderColor: color,
        backgroundColor: hexToRgba(color, 0.12),
        fill: true,
        tension: 0.36,
        pointRadius: 3,
        borderWidth: 2
      };
    });
    const canvas = document.getElementById('salesChart');
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    try { if (dailySalesChart) dailySalesChart.destroy(); } catch (e) { }
    dailySalesChart = new Chart(ctx, {
      type: 'line',
      data: { labels: labels, datasets: datasets },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        plugins: { legend: { position: 'top', labels: { usePointStyle: true } }, tooltip: { mode: 'index', intersect: false } },
        interaction: { mode: 'index', intersect: false },
        scales: { x: { grid: { color: '#f1f1f1' } }, y: { beginAtZero: true, grid: { color: '#f1f1f1' } } }
      }
    });
  }

  /* ---------- Main loader ---------- */
  async function loadTilesAndCharts(range = 7) {
    if (!priceTilesEl) return;
    try {
      const [pJson, dJson, psJson] = await Promise.all([
        fetchJson(api + '?action=get_products'),
        fetchJson(api + '?action=get_dashboard&range=' + range),
        fetchJson(api + '?action=get_products_sales&range=' + range)
      ]);

      priceTilesEl.innerHTML = '';
      const productsMap = {};
      if (pJson && pJson.ok && Array.isArray(pJson.products)) {
        pJson.products.forEach((pr, i) => { 
          priceTilesEl.insertAdjacentHTML('beforeend', productTileHtml(pr, i)); 
          productsMap[pr.id] = pr; 
        });
      }

      if (psJson && psJson.ok && Array.isArray(psJson.products)) {
        await renderProductSalesChart(psJson.products, productsMap);
      } else {
        await renderProductSalesChart([], productsMap);
      }

      try {
        const daily = await fetchJson(api + '?action=get_products_daily&range=' + range);
        if (daily && daily.ok && Array.isArray(daily.labels) && Array.isArray(daily.series)) {
          await renderDailySalesChart(daily, productsMap);
        } else if (dJson && dJson.labels && dJson.data) {
          await renderDailySalesChart({ labels: dJson.labels, series: [{ product_id: 0, name: 'Ventas', data: dJson.data }] }, productsMap);
        }
      } catch (e) {
        if (dJson && dJson.labels && dJson.data) {
          await renderDailySalesChart({ labels: dJson.labels, series: [{ product_id: 0, name: 'Ventas', data: dJson.data }] }, productsMap);
        }
      }

      const luEl = document.getElementById('lastUpdate');
      if (luEl) luEl.innerText = (psJson && psJson.updated_at) || (dJson && dJson.updated_at) || new Date().toLocaleString();
    } catch (e) {
      console.error('loadTilesAndCharts error', e);
    }
  }

  /* ---------- Recent sales ---------- */
  function loadRecentSales() {
    const recentList = document.getElementById('recentList');
    if (!recentList) return;
    
    const xhr = new XMLHttpRequest();
    xhr.open('GET', api + '?action=get_recent_sales&limit=20&t=' + Date.now(), true);
    xhr.onload = function() {
      if (xhr.status === 200) {
        try {
          const r = JSON.parse(xhr.responseText);
          if (r && r.ok && Array.isArray(r.sales)) {
            let html = '<div class="list-group">';
            r.sales.forEach(s => {
              html += '<div class="list-group-item d-flex justify-content-between align-items-center">';
              html += '<div><div><strong>' + esc(s.sale_code) + '</strong></div><div class="small text-muted">' + esc(s.created_at) + ' — ' + esc(s.username || 'Sistema') + '</div></div>';
              html += '<div class="fw-semibold">' + esc(fmtCurrency(s.total)) + '</div></div>';
            });
            html += '</div>';
            recentList.innerHTML = html;
          }
        } catch(e) {}
      }
    };
    xhr.send();
  }

  /* ---------- Pumps ---------- */
  async function loadPumpsFull() {
    if (!pumpsGrid) return;
    pumpsGrid.innerHTML = '<div class="text-muted">Cargando islas...</div>';
    try {
      const r = await fetchJson(api + '?action=get_pumps_unified');
      if (r && r.ok && Array.isArray(r.pumps) && r.pumps.length) {
        pumpsGrid.innerHTML = '';
        r.pumps.sort((a, b) => a.id - b.id).forEach(p => pumpsGrid.insertAdjacentHTML('beforeend', pumpCardHtml(p)));
        return;
      }
      pumpsGrid.innerHTML = '<div class="text-muted">No hay islas registradas</div>';
    } catch (e) {
      console.error('loadPumpsFull error', e);
      pumpsGrid.innerHTML = '<div class="text-danger">Error cargando islas</div>';
    }
  }

  /* ---------- Sales modal ---------- */
  function renderSalesModal(sales) {
    if (!Array.isArray(sales) || sales.length === 0) {
      psModalContent.innerHTML = '<div class="sales-no-data">No se encontraron ventas para esta bomba.</div>';
      return;
    }
    let html = '<div class="mb-2 small text-muted">Filtrar por ticket o producto — resultados: <span id="psMatches">' + sales.length + '</span></div>';
    sales.forEach((sale) => {
      const saleId = esc(String(sale.sale_id));
      const saleCode = esc(sale.sale_code || sale.sale_id);
      const dt = fmtDateTime(sale.created_at || '');
      const dPart = esc(dt[0]), tPart = esc(dt[1]);
      let saleTotal = 0;
      (sale.items || []).forEach(it => { saleTotal += parseFloat(it.total || 0); });

      html += '<div class="sale-card mb-3" data-sale-id="' + saleId + '">';
      html += '<div class="d-flex justify-content-between align-items-center p-2" style="border:1px solid #eee;border-radius:6px;">';
      html += '<div><strong class="sale-code">' + saleCode + '</strong><br><small class="text-muted sale-dt">' + dPart + ' ' + tPart + '</small></div>';
      html += '<div class="d-flex align-items-center gap-2">';
      html += '<div class="fw-semibold sale-total">' + esc(fmtCurrency(saleTotal)) + '</div>';
      html += '<button class="btn btn-sm btn-outline-secondary toggle-items" data-target="' + saleId + '">Ocultar</button>';
      html += '<button class="btn btn-sm btn-primary print-btn" data-sale-id="' + saleId + '">Imprimir</button>';
      html += '</div></div>';

      html += '<div class="sale-items mt-2" data-target="' + saleId + '">';
      html += '<table class="sales-table"><thead><tr><th style="width:70px">Cantidad</th><th>Producto</th><th style="width:110px">Precio unit.</th><th style="width:110px" class="text-end">Valor</th></tr></thead><tbody>';
      (sale.items || []).forEach(it => {
        const qty = esc(it.qty);
        const prod = esc(it.product_name || it.name || '');
        const up = (typeof it.unit_price !== 'undefined') ? esc(fmtCurrency(it.unit_price)) : '—';
        const val = esc(fmtCurrency(it.total));
        html += '<tr class="sale-item-row"><td>' + qty + '</td><td>' + prod + '</td><td>' + up + '</td><td class="text-end">' + val + '</td></tr>';
      });
      html += '</tbody></table></div></div>';
    });

    psModalContent.innerHTML = html;

    psModalContent.querySelectorAll('.toggle-items').forEach(btn => {
      btn.addEventListener('click', function () {
        const tid = btn.getAttribute('data-target');
        const cont = psModalContent.querySelector('.sale-items[data-target="' + tid + '"]');
        if (!cont) return;
        const isHidden = cont.style.display === 'none';
        cont.style.display = isHidden ? '' : 'none';
        btn.textContent = isHidden ? 'Ocultar' : 'Ver';
      });
    });

    psModalContent.querySelectorAll('.print-btn').forEach(btn => {
      btn.addEventListener('click', function () {
        const sid = btn.getAttribute('data-sale-id');
        const saleObj = sales.find(s => String(s.sale_id) === String(sid));
        if (saleObj) { 
          printSaleWindow(saleObj).catch(err => { 
            console.error('printSaleWindow error', err); 
            alert('Error al imprimir'); 
          }); 
        } else {
          alert('Venta no encontrada para impresión');
        }
      });
    });

    if (psSearch) {
      psSearch.value = '';
      psSearch.oninput = function () {
        const q = String(psSearch.value || '').trim().toLowerCase();
        let matches = 0;
        psModalContent.querySelectorAll('.sale-card').forEach(card => {
          const saleCode = (card.querySelector('.sale-code')?.textContent || '').toLowerCase();
          const productText = Array.from(card.querySelectorAll('.sale-item-row td:nth-child(2)')).map(td => td.textContent || '').join(' ').toLowerCase();
          const show = q === '' || saleCode.indexOf(q) !== -1 || productText.indexOf(q) !== -1;
          card.style.display = show ? '' : 'none';
          if (show) matches++;
        });
        const matchesEl = document.getElementById('psMatches');
        if (matchesEl) matchesEl.textContent = matches;
      };
    }

    if (psExpandAll) {
      psExpandAll.onclick = function () {
        psModalContent.querySelectorAll('.sale-items').forEach(el => el.style.display = '');
        psModalContent.querySelectorAll('.toggle-items').forEach(b => b.textContent = 'Ocultar');
      };
    }
    
    if (psCollapseAll) {
      psCollapseAll.onclick = function () {
        psModalContent.querySelectorAll('.sale-items').forEach(el => el.style.display = 'none');
        psModalContent.querySelectorAll('.toggle-items').forEach(b => b.textContent = 'Ver');
      };
    }

    if (psSearch) psSearch.focus();
  }

  /* ---------- Pump click handler ---------- */
  function attachPumpClickHandler() {
    if (!pumpsGrid) return;
    if (pumpsGrid._attached) return;
    pumpsGrid._attached = true;

    pumpsGrid.addEventListener('click', async function (ev) {
      const card = ev.target.closest('.pump-card');
      if (!card) return;
      const pumpId = card.getAttribute('data-pump-id');
      if (!pumpId) return;

      if (!psModal || !psModalTitle || !psModalSubtitle || !psModalContent) return;

      psModalTitle.textContent = 'Ventas - ' + (card.querySelector('.pump-name')?.textContent || ('Bomba ' + pumpId));
      psModalSubtitle.textContent = 'Cargando ventas...';
      psModalContent.innerHTML = '<div class="sales-no-data">Cargando...</div>';
      psModal.style.display = 'flex';

      try {
        try { await getStation(); } catch (e) { /* ignore */ }

        const json = await fetchJson(api + '?action=get_pump_sales_detailed&pump_id=' + encodeURIComponent(pumpId) + '&limit=500');
        if (!json || !json.ok) {
          psModalContent.innerHTML = '<div class="sales-no-data">Error al cargar ventas</div>';
          psModalSubtitle.textContent = '';
          return;
        }
        const sales = Array.isArray(json.sales) ? json.sales : [];
        renderSalesModal(sales);
        psModalSubtitle.textContent = (sales.length + ' ventas (más recientes arriba)');
      } catch (e) {
        console.error('pump click error', e);
        psModalContent.innerHTML = '<div class="sales-no-data">Error al cargar ventas.</div>';
        psModalSubtitle.textContent = '';
      }
    });
  }

  /* ---------- Print ---------- */
  async function printSaleWindow(sale) {
    const station = await getStation().catch(() => null);

    const dt = fmtDateTime(sale.created_at || '');
    const dPart = dt[0], tPart = dt[1];
    const name = station && (station.nombre || station.name) ? (station.nombre || station.name) : 'Estación de servicios';
    const address = station && (station.direccion || station.address) ? (station.direccion || station.address) : '';
    const phone = station && (station.telefono || station.phone) ? (station.telefono || station.phone) : '';
    const rnc = station && station.rnc ? station.rnc : '';
    const msg1 = station && station.mensaje1 ? station.mensaje1 : '';
    const msg2 = station && station.mensaje2 ? station.mensaje2 : '';

    let itemsHtml = '';
    let total = 0;
    (sale.items || []).forEach(it => {
      const qty = esc(it.qty);
      const prod = esc(it.product_name || it.name || '');
      const upVal = (typeof it.unit_price !== 'undefined' && it.unit_price !== null) 
        ? it.unit_price 
        : ((it.total && it.qty) ? (parseFloat(it.total) / Math.max(1, parseFloat(it.qty))) : 0);
      const up = esc(fmtCurrency(upVal));
      const lineTotal = parseFloat(it.total || 0);
      itemsHtml += '<tr><td style="width:50px">' + qty + '</td><td>' + prod + '</td><td style="text-align:right">' + up + '</td><td style="text-align:right">' + esc(fmtCurrency(lineTotal)) + '</td></tr>';
      total += lineTotal;
    });

    if ((!sale.items || sale.items.length === 0) && sale.pump_total) {
      itemsHtml += '<tr><td>-</td><td>Subtotal bomba</td><td></td><td style="text-align:right">' + esc(fmtCurrency(sale.pump_total)) + '</td></tr>';
      total = parseFloat(sale.pump_total || 0);
    }

    const lines = [];
    lines.push('<!doctype html>');
    lines.push('<html><head><meta charset="utf-8"><title>Ticket ' + esc(sale.sale_code || sale.sale_id) + '</title>');
    lines.push('<style>body{font-family:monospace;font-size:12px;width:320px;margin:0;padding:6mm}.center{text-align:center}table{width:100%;border-collapse:collapse}td,th{padding:3px 0;vertical-align:top}.bold{font-weight:700}hr{border:none;border-top:1px dashed #999;margin:8px 0}</style>');
    lines.push('</head><body>');

    lines.push('<div class="center"><div class="bold">' + esc(name) + '</div>');
    if (address) lines.push('<div>' + esc(address) + '</div>');
    if (phone) lines.push('<div>Tel: ' + esc(phone) + '</div>');
    if (rnc) lines.push('<div>RNC: ' + esc(rnc) + '</div>');
    lines.push('</div><hr>');

    lines.push('<div>Ticket: ' + esc(sale.sale_code || sale.sale_id) + '</div>');
    lines.push('<div>Fecha: ' + esc(dPart) + '   Hora: ' + esc(tPart) + '</div><hr>');

    lines.push('<table><thead><tr><th>Cant</th><th>Producto</th><th style="text-align:right">Precio</th><th style="text-align:right">Importe</th></tr></thead><tbody>');
    lines.push(itemsHtml);
    lines.push('</tbody></table><hr>');

    lines.push('<div style="display:flex;justify-content:space-between"><div>Total:</div><div class="bold">' + esc(fmtCurrency(total)) + '</div></div>');
    lines.push('<div style="height:8px"></div>');

    if (msg1) lines.push('<div class="center">' + esc(msg1) + '</div>');
    if (msg2) lines.push('<div class="center">' + esc(msg2) + '</div>');

    lines.push('<scr' + 'ipt>setTimeout(function(){window.print();setTimeout(function(){window.close();},300);},250);</scr' + 'ipt>');
    lines.push('</body></html>');

    const receipt = lines.join('\n');
    const w = window.open('', '_blank', 'width=420,height=640');
    if (!w) { alert('Permita ventanas emergentes para imprimir'); return; }
    w.document.open();
    w.document.write(receipt);
    w.document.close();
  }

  /* ---------- ⚡ Polling UNIFICADO CON XMLHttpRequest ---------- */
  function pollPumpsUnified() {
    if (!pumpsGrid) return;
    
    const xhr = new XMLHttpRequest();
    xhr.open('GET', api + '?action=get_pumps_unified&t=' + Date.now(), true);
    xhr.onload = function() {
      if (xhr.status === 200) {
        try {
          const r = JSON.parse(xhr.responseText);
          if (r && r.ok && Array.isArray(r.pumps)) {
            r.pumps.forEach(p => {
              const el = pumpsGrid.querySelector('[data-pump-id="' + p.id + '"]');
              if (!el) {
                pumpsGrid.insertAdjacentHTML('beforeend', pumpCardHtml(p));
                return;
              }

              const newStatus = (p.status || '').toString();
              const newLastId = p.last_sale ? String(p.last_sale.id) : '';
              const oldLastId = el.getAttribute('data-last-sale-id') || '';

              const newImg = getPumpImageByStatus(newStatus, p.image_url || pumpSvg);
              const imgEl = el.querySelector('.pump-image img');
              if (imgEl && imgEl.src !== newImg) {
                imgEl.src = newImg;
              }
              
              el.setAttribute('data-status', newStatus);
              
              const stEl = el.querySelector('.pump-status');
              if (stEl) stEl.textContent = newStatus || 'IDLE';

              if (oldLastId !== newLastId) {
                const lastAmtEl = el.querySelector('.pump-last');
                if (lastAmtEl) {
                  lastAmtEl.textContent = p.last_sale 
                    ? ('$' + parseFloat(p.last_sale.total).toFixed(2)) 
                    : '—';
                }
                
                const lastDtEl = el.querySelector('.pump-datetime');
                if (lastDtEl) {
                  if (p.last_sale) {
                    const lastDate = new Date(p.last_sale.created_at);
                    lastDtEl.textContent = new Intl.DateTimeFormat(undefined, { 
                      day: '2-digit', 
                      month: '2-digit', 
                      year: 'numeric', 
                      hour: '2-digit', 
                      minute: '2-digit', 
                      hour12: false 
                    }).format(lastDate);
                  } else {
                    lastDtEl.textContent = '';
                  }
                }
                
                el.setAttribute('data-last-sale-id', newLastId);
                el.classList.add('updated');
                setTimeout(() => el.classList.remove('updated'), 900);
              }
            });
          }
        } catch(e) {}
      }
    };
    xhr.send();
  }

  /* ---------- ⚡ SINCRONIZACIÓN ULTRA RÁPIDA CON XMLHttpRequest ---------- */
  let syncCount = 0;
  
  function syncSalesFromFile() {
    syncCount++;
    const thisSync = syncCount;
    const start = Date.now();
    
    const xhr = new XMLHttpRequest();
    xhr.open('GET', api + '?action=sync_sales_from_file&t=' + Date.now(), true);
    xhr.onload = function() {
      if (xhr.status === 200) {
        try {
          const result = JSON.parse(xhr.responseText);
          const elapsed = Date.now() - start;
          if (result && result.ok) {
            if (result.registered > 0) {
              console.log('[' + thisSync + '] ✅ ' + result.registered + ' ventas (' + elapsed + 'ms)');
              loadRecentSales();
            }
          }
        } catch(e) {}
      }
    };
    xhr.send();
  }

  /* ---------- Init ---------- */
  document.addEventListener('DOMContentLoaded', function () {
    priceTilesEl = document.getElementById('priceTiles');
    pumpsGrid = document.getElementById('pumpsGrid');
    psModal = document.getElementById('psModal');
    psModalBody = document.getElementById('psModalBody');
    psModalTitle = document.getElementById('psModalTitle');
    psModalSubtitle = document.getElementById('psModalSubtitle');
    psModalContent = document.getElementById('psModalContent');
    psModalClose = document.getElementById('psModalClose');
    psModalClose2 = document.getElementById('psModalClose2');
    psSearch = document.getElementById('psSearch');
    psExpandAll = document.getElementById('psExpandAll');
    psCollapseAll = document.getElementById('psCollapseAll');

    if (psModalClose) psModalClose.addEventListener('click', () => { if (psModal) psModal.style.display = 'none'; });
    if (psModalClose2) psModalClose2.addEventListener('click', () => { if (psModal) psModal.style.display = 'none'; });
    if (psModal) psModal.addEventListener('click', (e) => { if (e.target === psModal) psModal.style.display = 'none'; });

    attachPumpClickHandler();

    loadTilesAndCharts(7);
    loadRecentSales();
    loadPumpsFull();

    console.log('🚀 ULTRA RÁPIDO - Sync cada 500ms');
    
    // Ejecutar INMEDIATAMENTE
    syncSalesFromFile();
    pollPumpsUnified();
    
    // ⚡ ULTRA RÁPIDO: Cada 500ms (medio segundo)
    window.setInterval(syncSalesFromFile, 500);
    window.setInterval(pollPumpsUnified, 500);
    
    // Lista cada 2 segundos
    window.setInterval(loadRecentSales, 2000);
    
    // Charts cada 5 minutos
    window.setInterval(function() {
      const range = parseInt(document.getElementById('chartRange')?.value || 7, 10);
      loadTilesAndCharts(range);
    }, 300000);
    
    console.log('✅ CONFIGURADO: sync=500ms, pumps=500ms, recent=2s');
  });

  window.__FP = window.__FP || {};
  window.__FP.loadTilesAndCharts = loadTilesAndCharts;
  window.__FP.printSaleWindow = printSaleWindow;
  window.__FP.getStation = getStation;
  window.__FP.syncSalesFromFile = syncSalesFromFile;
  window.__FP.pollPumpsUnified = pollPumpsUnified;

})();