<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"/><script>
(function() {
  var cacheKey = 'wdna_csrf_token';
  var csrfReady = null;

  function getCurrentToken() {
    if (window.WDNA_CSRF_TOKEN) {
      return window.WDNA_CSRF_TOKEN;
    }

    var meta = document.querySelector('meta[name="csrf-token"]');
    if (meta && meta.getAttribute('content')) {
      return meta.getAttribute('content');
    }

    var input = document.querySelector('input[name="_csrf"]');
    if (input && input.value) {
      return input.value;
    }

    return '';
  }

  function applyCsrfToken(token) {
    if (!token) return;

    window.WDNA_CSRF_TOKEN = token;

    try {
      var meta = document.querySelector('meta[name="csrf-token"]');
      if (meta) {
        meta.setAttribute('content', token);
      }

      var inputs = document.querySelectorAll('input[name="_csrf"]');
      for (var i = 0; i < inputs.length; i++) {
        inputs[i].setAttribute('value', token);
        inputs[i].value = token;
      }
    } catch (e) {}
  }

  function patchBodyWithCsrf(body, token) {
    if (!token || !body) return body;

    try {
      if (window.FormData && body instanceof FormData) {
        if (!body.has('_csrf')) {
          body.append('_csrf', token);
        }
        return body;
      }

      if (window.URLSearchParams && body instanceof URLSearchParams) {
        if (!body.has('_csrf')) {
          body.append('_csrf', token);
        }
        return body;
      }

      if (typeof body === 'string') {
        var trimmed = body.trim();

        // Nao mexe em JSON. Algumas rotas quebram se adicionarmos campo extra.
        if (trimmed.charAt(0) === '{' || trimmed.charAt(0) === '[') {
          return body;
        }

        if (body.indexOf('_csrf=') === -1) {
          if (body.length > 0) {
            return body + '&_csrf=' + encodeURIComponent(token);
          }

          return '_csrf=' + encodeURIComponent(token);
        }
      }
    } catch (e) {}

    return body;
  }

  // [V2] Refatoracao de loadFreshCsrf:
  //   - Nao cacheia promise resolvida com string vazia.
  //   - Se cache do sessionStorage estiver vazio/expirado, refaz fetch.
  //   - Se o fetch falhar, limpa csrfReady para que proxima chamada tente de novo.
  function loadFreshCsrf(forceRefresh) {
    if (!forceRefresh && csrfReady) {
      return csrfReady;
    }

    if (!forceRefresh) {
      try {
        var cached = JSON.parse(sessionStorage.getItem(cacheKey) || 'null');
        if (cached && cached.token && cached.expires > Date.now()) {
          applyCsrfToken(cached.token);
          csrfReady = Promise.resolve(cached.token);
          return csrfReady;
        }
      } catch (e) {}
    }

    var p = fetch('/__csrf_refresh', {
      method: 'GET',
      credentials: 'same-origin',
      cache: 'no-store'
    })
      .then(function(response) {
        return response.json();
      })
      .then(function(data) {
        if (data && data.csrfToken) {
          applyCsrfToken(data.csrfToken);

          try {
            sessionStorage.setItem(cacheKey, JSON.stringify({
              token: data.csrfToken,
              expires: Date.now() + 60000
            }));
          } catch (e) {}

          return data.csrfToken;
        }

        // [V2] Token nao retornado: limpa csrfReady para permitir retry
        csrfReady = null;
        return '';
      })
      .catch(function() {
        // [V2] Erro de rede: limpa csrfReady para permitir retry
        csrfReady = null;
        return '';
      });

    csrfReady = p;
    return p;
  }

  // [V2] Forca refresh do token: invalida cache local e do sessionStorage,
  // chama /__csrf_refresh novamente.
  function forceRefreshCsrf() {
    csrfReady = null;
    try { sessionStorage.removeItem(cacheKey); } catch (e) {}
    return loadFreshCsrf(true);
  }

  function isUnsafeMethod(method) {
    return !/^(GET|HEAD|OPTIONS)$/i.test(method || 'GET');
  }

  // [V2] Helper: verifica se URL eh AJAX interno (mesma origem, path /ajax/* ou /checkout/*).
  // Apenas requests AJAX internos sao elegiveis para retry em 403.
  function isInternalAjax(input) {
    try {
      var url = (typeof input === 'string') ? input : (input && input.url) || '';
      if (!url) return false;

      // Mesma origem: comeca com / ou com a propria origin
      if (url.charAt(0) === '/') {
        return /^\/(ajax|checkout|busca|api)\//i.test(url) || url.indexOf('/ajax') === 0;
      }

      if (url.indexOf(window.location.origin) === 0) {
        var path = url.substring(window.location.origin.length);
        return /^\/(ajax|checkout|busca|api)\//i.test(path) || path.indexOf('/ajax') === 0;
      }
    } catch (e) {}
    return false;
  }

  if (window.fetch && !window.__WDNA_CSRF_FETCH_PATCHED__) {
    window.__WDNA_CSRF_FETCH_PATCHED__ = true;

    var originalFetch = window.fetch;

    window.fetch = function(input, init) {
      init = init || {};

      var method = (init.method || 'GET').toUpperCase();

      try {
        if (input && typeof input === 'object' && input.method && !init.method) {
          method = input.method.toUpperCase();
        }
      } catch (e) {}

      if (!isUnsafeMethod(method)) {
        return originalFetch.call(this, input, init);
      }

      var self = this;

      return loadFreshCsrf().then(function(token) {
        token = token || getCurrentToken();

        if (token) {
          init.headers = new Headers(init.headers || {});

          if (!init.headers.has('x-csrf-token')) init.headers.set('x-csrf-token', token);
          if (!init.headers.has('x-xsrf-token')) init.headers.set('x-xsrf-token', token);
          if (!init.headers.has('csrf-token')) init.headers.set('csrf-token', token);
          if (!init.headers.has('x-csrf')) init.headers.set('x-csrf', token);

          if (init.body) {
            init.body = patchBodyWithCsrf(init.body, token);
          }
        }

        return originalFetch.call(self, input, init).then(function(response) {
          // [V2] Retry transparente em 403 para AJAX interno
          if (response.status === 403 && isInternalAjax(input) && !init.__wdnaRetried) {
            return forceRefreshCsrf().then(function(newToken) {
              if (!newToken) return response;

              // Clona init e marca como retried para nao loopar
              var retryInit = Object.assign({}, init);
              retryInit.__wdnaRetried = true;
              retryInit.headers = new Headers(init.headers || {});
              retryInit.headers.set('x-csrf-token', newToken);
              retryInit.headers.set('x-xsrf-token', newToken);
              retryInit.headers.set('csrf-token', newToken);
              retryInit.headers.set('x-csrf', newToken);

              if (retryInit.body && retryInit.body !== init.body) {
                // body ja foi consumido em alguns casos; reusa o de init
              }
              if (init.body) {
                retryInit.body = patchBodyWithCsrf(init.body, newToken);
              }

              return originalFetch.call(self, input, retryInit);
            });
          }
          return response;
        });
      });
    };
  }

  if (window.XMLHttpRequest && !window.__WDNA_CSRF_XHR_PATCHED__) {
    window.__WDNA_CSRF_XHR_PATCHED__ = true;

    var originalOpen = XMLHttpRequest.prototype.open;
    var originalSend = XMLHttpRequest.prototype.send;
    var originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;

    // [V2] Captura headers customizados setados pelo cliente antes do send,
    // para que possamos repeti-los em caso de retry em 403.
    XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
      try {
        this.__wdna_headers = this.__wdna_headers || [];
        this.__wdna_headers.push([name, value]);
      } catch (e) {}
      return originalSetRequestHeader.apply(this, arguments);
    };

    XMLHttpRequest.prototype.open = function(method, url) {
      this.__wdna_method = method || 'GET';
      this.__wdna_url = url || '';
      this.__wdna_headers = [];
      return originalOpen.apply(this, arguments);
    };

    XMLHttpRequest.prototype.send = function(body) {
      var xhr = this;
      var method = (xhr.__wdna_method || 'GET').toUpperCase();

      if (!isUnsafeMethod(method)) {
        return originalSend.apply(xhr, arguments);
      }

      var args = arguments;
      var originalBody = body;

      // [V2] Salva referencias para retry
      xhr.__wdna_original_body = originalBody;
      xhr.__wdna_retried = false;

      loadFreshCsrf().then(function(token) {
        token = token || getCurrentToken();

        if (token) {
          try {
            originalSetRequestHeader.call(xhr, 'x-csrf-token', token);
            originalSetRequestHeader.call(xhr, 'x-xsrf-token', token);
            originalSetRequestHeader.call(xhr, 'csrf-token', token);
            originalSetRequestHeader.call(xhr, 'x-csrf', token);
          } catch (e) {}

          if (args && args.length > 0 && args[0]) {
            args[0] = patchBodyWithCsrf(args[0], token);
            xhr.__wdna_patched_body = args[0];
          }
        }

        // [V2] Hook para detectar 403 e tentar refresh + retry uma vez
        try {
          var originalOnReadyStateChange = xhr.onreadystatechange;
          xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 403 &&
                !xhr.__wdna_retried &&
                isInternalAjax(xhr.__wdna_url)) {

              xhr.__wdna_retried = true;

              // Cria um novo XHR para o retry (nao da pra reabrir o mesmo)
              forceRefreshCsrf().then(function(newToken) {
                if (!newToken) {
                  if (originalOnReadyStateChange) {
                    try { originalOnReadyStateChange.apply(xhr, arguments); } catch (e) {}
                  }
                  return;
                }

                var retryXhr = new XMLHttpRequest();
                retryXhr.__wdna_retried = true; // marca para nao re-retry

                retryXhr.open(xhr.__wdna_method, xhr.__wdna_url, true);

                // Reaplica headers que o usuario tinha setado
                try {
                  for (var i = 0; i < (xhr.__wdna_headers || []).length; i++) {
                    var h = xhr.__wdna_headers[i];
                    if (!/^(x-csrf-token|x-xsrf-token|csrf-token|x-csrf)$/i.test(h[0])) {
                      retryXhr.setRequestHeader(h[0], h[1]);
                    }
                  }
                  retryXhr.setRequestHeader('x-csrf-token', newToken);
                  retryXhr.setRequestHeader('x-xsrf-token', newToken);
                  retryXhr.setRequestHeader('csrf-token', newToken);
                  retryXhr.setRequestHeader('x-csrf', newToken);
                } catch (e) {}

                // Repropaga eventos para o XHR original
                retryXhr.onreadystatechange = function() {
                  if (retryXhr.readyState === 4) {
                    // Copia status e response para o xhr original via redefinicao
                    try {
                      Object.defineProperty(xhr, 'status', { value: retryXhr.status, configurable: true });
                      Object.defineProperty(xhr, 'statusText', { value: retryXhr.statusText, configurable: true });
                      Object.defineProperty(xhr, 'responseText', { value: retryXhr.responseText, configurable: true });
                      Object.defineProperty(xhr, 'response', { value: retryXhr.response, configurable: true });
                      if (retryXhr.responseType === '' || retryXhr.responseType === 'text') {
                        Object.defineProperty(xhr, 'responseXML', { value: retryXhr.responseXML, configurable: true });
                      }
                    } catch (e) {}

                    if (originalOnReadyStateChange) {
                      try { originalOnReadyStateChange.apply(xhr, arguments); } catch (e) {}
                    }
                  }
                };

                var retryBody = xhr.__wdna_original_body;
                if (retryBody) {
                  retryBody = patchBodyWithCsrf(retryBody, newToken);
                }
                retryXhr.send(retryBody);
              });

              return; // nao chama o onReadyStateChange original ainda
            }

            if (originalOnReadyStateChange) {
              try { originalOnReadyStateChange.apply(xhr, arguments); } catch (e) {}
            }
          };
        } catch (e) {}

        return originalSend.apply(xhr, args);
      }).catch(function() {
        // Se loadFreshCsrf falhar catastroficamente, ainda tenta enviar (sem token)
        // para nao quebrar a pagina inteira
        try {
          return originalSend.apply(xhr, args);
        } catch (e) {}
      });
    };
  }

  loadFreshCsrf();
})();
</script>
