<?php // includes/footer.php — incluir al final de cada página (una sola vez) ?>
<footer class="text-center small text-muted py-3 border-top mt-4">
  &copy; <?= date('Y') ?> VEGAPC 23 &middot; I.E.P. NOBEL INGENIEROS — Todos los derechos reservados.
</footer>

<!-- ========================
     JS global (ORDEN CORRECTO)
     ======================== -->

<!-- jQuery -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7/dist/jquery.min.js"></script>

<!-- Bootstrap 5.x (bundle con Popper) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>

<!-- DataTables core -->
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>

<!-- Extensiones DataTables -->
<script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.bootstrap5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.print.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.colVis.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10/dist/jszip.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pdfmake@0.2/dist/pdfmake.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pdfmake@0.2/build/vfs_fonts.js"></script>
<script src="https://cdn.datatables.net/responsive/2.5.0/js/dataTables.responsive.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.5.0/js/responsive.bootstrap5.min.js"></script>
<script src="https://cdn.datatables.net/fixedheader/3.4.0/js/dataTables.fixedHeader.min.js"></script>
<script src="https://cdn.datatables.net/select/1.7.0/js/dataTables.select.min.js"></script>
<script src="https://cdn.datatables.net/colreorder/1.7.0/js/dataTables.colReorder.min.js"></script>
<script src="https://cdn.datatables.net/rowgroup/1.4.1/js/dataTables.rowGroup.min.js"></script>
<script src="https://cdn.datatables.net/rowreorder/1.4.1/js/dataTables.rowReorder.min.js"></script>
<script src="https://cdn.datatables.net/scroller/2.3.0/js/dataTables.scroller.min.js"></script>
<script src="https://cdn.datatables.net/fixedcolumns/4.3.0/js/dataTables.fixedColumns.min.js"></script>
<script src="https://cdn.datatables.net/keytable/2.10.0/js/dataTables.keyTable.min.js"></script>

<!-- ==========================================================
     FIX MÓVIL: OFFCANVAS / BACKDROP / SCROLL BLOQUEADO
     (esto apunta DIRECTO a tu problema “no me deja desplazar”)
     - En algunos móviles al cerrar el offcanvas queda overflow:hidden
     - O queda un .offcanvas-backdrop “huérfano” que tapa la pantalla
     ========================================================== -->
<script>
(function(){
  // Si el menú hamburguesa usa Offcanvas de Bootstrap:
  // 1) al cerrar, limpiar overflow del body y eliminar backdrops viejos
  document.addEventListener('hidden.bs.offcanvas', function(){
    try{
      document.body.style.overflow = '';
      document.body.classList.remove('offcanvas-backdrop-fix');
      document.querySelectorAll('.offcanvas-backdrop').forEach(function(b){ b.remove(); });
    }catch(e){}
  }, true);

  // 2) al abrir, marcar clase (si tu CSS ayuda con overflow)
  document.addEventListener('shown.bs.offcanvas', function(){
    try{ document.body.classList.add('offcanvas-backdrop-fix'); }catch(e){}
  }, true);

  // 3) si por cualquier razón quedaron backdrops al cargar, limpiarlos
  document.addEventListener('DOMContentLoaded', function(){
    try{
      // Si no hay offcanvas abierto pero hay backdrops, bórralos
      var anyOpen = document.querySelector('.offcanvas.show');
      if (!anyOpen) document.querySelectorAll('.offcanvas-backdrop').forEach(function(b){ b.remove(); });
    }catch(e){}
  });
})();
</script>

<!-- Guard de Bootstrap + hook del botón del modal -->
<script>
  (function () {
    function onReady(fn){
      if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn);
      else fn();
    }
    // NOTA: ya cargas bootstrap.bundle arriba, así que NO lo volvemos a inyectar.
    function ensureBootstrap(cb){
      if (window.bootstrap && window.bootstrap.Modal) { cb && cb(); return; }
      // Fallback extremo (solo si por alguna razón no cargó)
      var s = document.createElement('script');
      s.src = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js";
      s.onload = function(){ cb && cb(); };
      document.head.appendChild(s);
    }
    onReady(function(){
      ensureBootstrap(function(){
        var btn = document.getElementById('btnTomadoGeneral');
        var modalEl = document.getElementById('modalTomadoGeneral');
        if (btn && modalEl) {
          btn.addEventListener('click', function(){
            try { bootstrap.Modal.getOrCreateInstance(modalEl).show(); } catch(e) { console.error(e); }
          });
        }
      });
    });
  })();
</script>

<!-- ========================
     Mejores prácticas globales
     ======================== -->
<script>
(function(){
  // ========= Utilidad: título para exportaciones (DataTables) =========
  function exportTitle(){
    var d = new Date();
    var yyyy = d.getFullYear(), mm = ('0'+(d.getMonth()+1)).slice(-2), dd = ('0'+d.getDate()).slice(-2);
    var page = (document.title || 'Reporte').replace(/[\r\n]+/g,' ').trim();
    return page + ' — ' + yyyy + '-' + mm + '-' + dd;
  }

  // ========= DataTables: idioma ES y defaults =========
  if (window.jQuery && $.fn && $.fn.dataTable) {
    $.extend(true, $.fn.dataTable.Buttons.defaults, {
      dom: { button: { className: 'btn btn-sm btn-outline-secondary me-2' } }
    });

    $.extend(true, $.fn.dataTable.defaults, {
      responsive: true,
      processing: true,
      deferRender: true,
      language: {
        url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/es-ES.json'
      },
      pageLength: 25,
      lengthMenu: [[10,25,50,100,-1],[10,25,50,100,'Todos']]
    });

    // Auto-init: .datatable o [data-dt]
    $(function(){
      $('table.datatable, table[data-dt]').each(function(){
        var $t = $(this);
        if ($.fn.dataTable.isDataTable(this)) return;

        var withButtons = $t.is('.dt-export, [data-dt~="export"]');
        var options = {};

        if (withButtons) {
          options.dom = "<'row'<'col-sm-6'l><'col-sm-6 text-end'B>>" +
                        "<'row'<'col-12'tr>>" +
                        "<'row'<'col-sm-5'i><'col-sm-7'p>>";
          options.buttons = [
            {extend:'copyHtml5',  title: exportTitle()},
            {extend:'excelHtml5', title: exportTitle(), filename: exportTitle().replace(/\s+/g,'_')},
            {extend:'pdfHtml5',   title: exportTitle(), filename: exportTitle().replace(/\s+/g,'_'), orientation:'landscape', pageSize:'A4'},
            {extend:'print',      title: exportTitle()},
            {extend:'colvis',     text: 'Columnas'}
          ];
        } else {
          options.dom = "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
                        "<'row'<'col-12'tr>>" +
                        "<'row'<'col-sm-5'i><'col-sm-7'p>>";
        }

        try {
          var extra = $t.data('dt-options');
          if (extra && typeof extra === 'object') { options = $.extend(true, {}, options, extra); }
        } catch(e){}

        $t.DataTable(options);
      });
    });
  }

  // ========= Guard: evita doble envío de formularios marcados =========
  document.addEventListener('submit', function(e){
    var f = e.target;
    if (!f.classList || !f.classList.contains('js-guard-submit')) return;
    if (f.dataset.submitted === '1') {
      e.preventDefault(); e.stopPropagation();
      if (window.SwalToast) SwalToast('info','Ya se envió, espera…');
      return;
    }
    f.dataset.submitted = '1';
    setTimeout(function(){ delete f.dataset.submitted; }, 6000);
  }, true);

  // ========= Disparadores rápidos de SweetAlert2 por atributos =========
  document.addEventListener('click', function(ev){
    var t = ev.target.closest('[data-toast], [data-alert]');
    if (!t) return;
    if (t.dataset.autoconfirm === '0') return;

    var type  = t.dataset.toast || null;
    var title = t.dataset.title || t.title || 'Listo';
    var text  = t.dataset.text || '';
    var icon  = (t.dataset.icon || type || 'success');

    if (t.dataset.toast){
      ev.preventDefault();
      if (window.SwalToast) SwalToast(icon, title);
      return;
    }
    if (t.dataset.alert){
      ev.preventDefault();
      if (window.Swal) Swal.fire({icon: icon, title: title, text: text});
    }
  }, true);

  // ========= Convertir alerts HTML a SweetAlert2 (opt-in con data-swal) =========
  document.addEventListener('DOMContentLoaded', function(){
    document.querySelectorAll('.alert[data-swal]').forEach(function(a){
      var text = a.textContent.trim();
      var isOk  = a.classList.contains('alert-success');
      var isErr = a.classList.contains('alert-danger') || a.classList.contains('alert-warning');
      if (window.Swal) {
        if (isOk && window.SwalToast) SwalToast('success', text);
        else Swal.fire({icon: isErr ? 'error' : 'info', title: text});
      }
      a.remove();
    });
  });

  /* ====== AJAX global: headers + manejo de errores (AJUSTADO) ====== */
  function parseJsonSafe(xhr){
    try{
      var ct = (xhr.getResponseHeader && xhr.getResponseHeader('Content-Type')) || '';
      if (ct && ct.indexOf('application/json') !== -1 && xhr.responseText) {
        return JSON.parse(xhr.responseText);
      }
    }catch(e){}
    return null;
  }
  function cleanHtml(s){
    return String(s||'').replace(/<[^>]+>/g,' ').replace(/\s+/g,' ').trim();
  }

  if (window.jQuery && $.ajaxSetup) {
    $.ajaxSetup({ headers: {'X-Requested-With': 'XMLHttpRequest'} });

    $(document).ajaxError(function(_e, xhr, settings){
      var url = (settings && settings.url) ? settings.url : '';

      // Ignorar errores de recursos externos (CDN, idioma DataTables, etc.)
      if (url &&
          (url.indexOf('cdn.datatables.net') !== -1 ||
           url.indexOf('cdn.jsdelivr.net')   !== -1 ||
           url.indexOf('googleapis.com')     !== -1)) {
        return;
      }

      var j = parseJsonSafe(xhr);
      var msg = (j && (j.error || j.message)) ||
                (xhr && typeof xhr.responseText === 'string' ? cleanHtml(xhr.responseText).slice(0,400) : '') ||
                'Se produjo un error de comunicación.';

      if (xhr.status === 0) {
        if (window.Swal)
          return Swal.fire({icon:'error', title:'Sin conexión', text:'No se pudo contactar al servidor.'});
      } else if (xhr.status === 401) {
        if (window.Swal)
          Swal.fire({icon:'error', title:'Sesión expirada', text:'Vuelve a iniciar sesión.'})
            .then(()=> location.href = '/sistema_ventas/login.php');
        else location.href = '/sistema_ventas/login.php';
        return;
      } else if (xhr.status === 403) {
        if (window.Swal) return Swal.fire({icon:'error', title:'Operación no permitida', html: msg});
      } else if (xhr.status === 404) {
        if (window.Swal) return Swal.fire({icon:'error', title:'Recurso no encontrado', text:'La ruta solicitada no existe.'});
      } else if (xhr.status >= 500) {
        if (window.Swal) return Swal.fire({icon:'error', title:'Error del servidor', html: msg});
      }

      if (window.Swal) Swal.fire({icon:'error', title:'Error', html: msg});
    });
  }

  /* ===== Wrapper para fetch ===== */
  window.apiFetch = async function(input, init){
    init = init || {};
    init.headers = Object.assign({'X-Requested-With':'fetch'}, init.headers || {});
    const r = await fetch(input, init);

    if (r.status === 401){
      if (window.Swal) await Swal.fire({icon:'error', title:'Sesión expirada', text:'Vuelve a iniciar sesión.'});
      location.href = '/sistema_ventas/login.php';
      throw new Error('401 Unauthorized');
    }
    if (r.status === 403){
      let msg = 'Operación no permitida.';
      try { const j = await r.clone().json(); msg = j.error || j.message || msg; } catch(_){}
      if (window.Swal) await Swal.fire({icon:'error', title:'Acceso denegado', html: msg});
      throw new Error('403 Forbidden');
    }
    if (r.status >= 500){
      let msg = 'Error del servidor.';
      try { const j = await r.clone().json(); msg = j.error || j.message || msg; } catch(_){}
      if (window.Swal) await Swal.fire({icon:'error', title:'Error del servidor', html: msg});
      throw new Error(String(r.status));
    }
    return r;
  };

  // ===== Captura de errores JS no manejados =====
  window.addEventListener('unhandledrejection', function(e){
    if (!window.Swal) return;
    const m = (e && e.reason && (e.reason.message || e.reason)) || 'Error inesperado.';
    Swal.fire({icon:'error', title:'Error', text: String(m)});
  });

})();
</script>

<?php if (!empty($customJs) && is_array($customJs)): ?>
  <?php foreach ($customJs as $src): ?>
    <script src="<?= htmlspecialchars($src, ENT_QUOTES, 'UTF-8') ?>"></script>
  <?php endforeach; ?>
<?php endif; ?>

<?php if (!empty($customInlineJs)): ?>
  <script><?= $customInlineJs ?></script>
<?php endif; ?>

<!-- ==============================
     Implementación adicional
     ============================== -->
<script>
(function(){
  function ensureSwal(cb){
    if (window.Swal) { cb && cb(); return; }
    var s = document.createElement('script');
    s.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11';
    s.onload = function(){ cb && cb(); };
    document.head.appendChild(s);
  }
  function defineHelpers(){
    if (!window.SwalToast) {
      window.SwalToast = function(icon, title, timer){
        return Swal.fire({
          toast:true,
          icon:icon||'success',
          title:title||'Listo',
          timer: timer||2200,
          timerProgressBar:true,
          showConfirmButton:false,
          position:'top'
        });
      };
    }
    if (!window.SwalConfirm) {
      window.SwalConfirm = function(opts){
        opts = opts || {};
        return Swal.fire({
          title: opts.title || '¿Confirmar acción?',
          text:  opts.text  || '',
          icon:  opts.icon  || 'warning',
          showCancelButton: true,
          confirmButtonText: opts.confirmText || 'Sí',
          cancelButtonText:  opts.cancelText  || 'Cancelar',
          reverseButtons: true,
          customClass: { confirmButton: 'btn btn-primary', cancelButton: 'btn btn-secondary' },
          buttonsStyling: false
        });
      };
    }
  }
  function refreshFaviconOnce(){
    try{
      var key = 'faviconRefreshed';
      if (localStorage.getItem(key) === '1') return;
      var links = document.querySelectorAll('link[rel~="icon"], link[rel="shortcut icon"], link[rel="apple-touch-icon"]');
      var stamp = Date.now();
      links.forEach(function(l){
        if (!l.href) return;
        var u = new URL(l.href, location.origin);
        if (!u.searchParams.has('v')) {
          u.searchParams.set('v', stamp);
          l.href = u.toString();
        }
      });
      localStorage.setItem(key, '1');
    } catch(e){}
  }
  ensureSwal(function(){ defineHelpers(); refreshFaviconOnce(); });
})();
</script>

</body>
</html>
