// site-jogador/js/atribuicao.jsx
//
// DE ONDE A PESSOA VEIO — captura de origem de campanha.
//
// POR QUE ISSO EXISTE
//   01/09/2026: o Henrique gastou R$ 50 num anuncio no YouTube (26-29/08) que o
//   proprio YouTube diz ter mandado ~800 pessoas pro site, e perguntou quantas
//   viraram jogador. A resposta foi "nao da pra saber": o site nao tinha
//   analytics nenhum e o cadastro nunca gravou origem. So deu pra estimar por
//   janela de data, que mistura o anuncio com o Discord e com o organico.
//
// COMO USAR NUMA CAMPANHA
//   Basta o link do anuncio carregar os parametros:
//     https://projectgenz.com.br/?utm_source=youtube&utm_medium=cpc&utm_campaign=video-set26
//   Depois:  python scripts/relatorio_campanha.py
//
// ─────────────────────────────────────────────────────────────────────────
// AS TRES DECISOES QUE FAZEM ISSO FUNCIONAR
//
// 1. PRIMEIRO TOQUE, GUARDADO NO NAVEGADOR.
//    O login e OAuth do Discord com `redirectTo: origin + '/'` — a pessoa sai
//    do site e VOLTA SEM A QUERY STRING. Se a leitura fosse na hora do cadastro,
//    todo utm_ teria sumido no caminho. Por isso grava em localStorage na
//    primeira visita e so' manda pro banco depois que a sessao existe.
//
// 2. NUNCA SOBRESCREVE.
//    Quem clica no anuncio, some, e volta uma semana depois pelo Discord tem que
//    continuar contando pro anuncio. Sobrescrever faria toda campanha perder o
//    credito pro organico que vem depois — o erro que faz anuncio bom parecer
//    ruim. O `ON CONFLICT DO NOTHING` do banco e a segunda trava da mesma regra.
//
// 3. NAO GUARDA NADA DE PESSOAL.
//    So' os utm_, o dominio de onde veio (o referrer entra sem query string, que
//    e' onde vazam token e busca) e o caminho de entrada.

(function gzAtribuicao() {
  if (typeof window === 'undefined') return;

  var CHAVE = 'gz_origem_v1';
  var CHAVE_ENVIADO = 'gz_origem_enviada_v1';
  var CAMPOS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];

  function ler(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }
  function gravar(k, v) { try { localStorage.setItem(k, v); } catch (e) { /* modo anonimo */ } }

  // ── 1. captura, uma vez por navegador ────────────────────────────────
  function capturar() {
    if (ler(CHAVE)) return;  // primeiro toque ja registrado: nao encosta

    var q = new URLSearchParams(window.location.search);
    var o = {};
    var temUtm = false;
    for (var i = 0; i < CAMPOS.length; i++) {
      var v = q.get(CAMPOS[i]);
      if (v) { o[CAMPOS[i]] = String(v).slice(0, 200); temUtm = true; }
    }

    // referrer SEM query string: o caminho completo pode carregar token de
    // sessao e termo de busca de outro site, e nada disso e' da nossa conta.
    var ref = '';
    try {
      if (document.referrer && new URL(document.referrer).origin !== window.location.origin) {
        var u = new URL(document.referrer);
        ref = u.origin + u.pathname;
      }
    } catch (e) { /* referrer torto */ }

    // visita organica direta nao vira registro: gravar tudo encheria a tabela
    // de "(direto)" e o primeiro toque de quem CHEGA por anuncio depois seria
    // bloqueado pelo proprio registro vazio.
    if (!temUtm && !ref) return;

    o.referrer = ref;
    o.landing = String(window.location.pathname || '/').slice(0, 500);
    o.capturado_em = new Date().toISOString();
    gravar(CHAVE, JSON.stringify(o));
  }

  // ── 2. entrega, quando existir sessao ────────────────────────────────
  function entregar() {
    if (ler(CHAVE_ENVIADO)) return;
    var bruto = ler(CHAVE);
    if (!bruto || !window.sb || !window.sb.rpc) return;

    var o;
    try { o = JSON.parse(bruto); } catch (e) { return; }

    window.sb.auth.getSession().then(function (r) {
      if (!r || !r.data || !r.data.session) return;   // ainda deslogado: tenta depois
      return window.sb.rpc('genz_registrar_origem', { p_origem: o }).then(function (res) {
        // marca como entregue mesmo quando o banco responde `false` (ja havia
        // origem gravada): insistir a cada carga so' geraria chamada inutil.
        if (!res || !res.error) gravar(CHAVE_ENVIADO, '1');
      });
    }).catch(function () { /* sem rede: fica pra proxima visita */ });
  }

  capturar();

  // ── 3. conta a CHEGADA, mesmo de quem nunca vai cadastrar ─────────────
  // Sem isto, o anuncio que manda 800 pessoas e o que manda 0 sao iguais pro
  // nosso lado (foi o caso de agosto). Anonimo: so' campanha + origem. O
  // dedupe e' aqui, por dia — F5 nao vira visita nova.
  function contarChegada() {
    var q = new URLSearchParams(window.location.search);
    var camp = q.get('utm_campaign');
    if (!camp || !window.sb || !window.sb.rpc) return;
    var hoje = new Date().toISOString().slice(0, 10);
    var marca = 'gz_visita_' + camp + '_' + hoje;
    if (ler(marca)) return;
    window.sb.rpc('genz_registrar_visita', {
      p_campanha: String(camp).slice(0, 80),
      p_origem: String(q.get('utm_source') || '').slice(0, 80) || null,
    }).then(function (res) {
      if (!res || !res.error) gravar(marca, '1');
    }).catch(function () { /* sem rede: nao conta, e nao insiste */ });
  }
  contarChegada();

  // ── 4. QUAL convite do Discord mostrar — decidido UMA vez, pra todas as portas
  // O site tem 4 botoes/links pro Discord (portao, 2 CTAs da home, rodape). Se
  // so' um deles trocasse pro convite da campanha, quem entrasse por outro
  // cairia no convite geral e a campanha perderia o credito no Discord. Aqui
  // e' o UNICO lugar do site com o endereco do convite; todo botao chama
  // window.gzDiscordUrl() na hora de renderizar e escuta a troca.
  var DISCORD_GERAL = 'https://discord.gg/mxDCpdGaGJ';
  var discordAtual = DISCORD_GERAL;
  window.gzDiscordUrl = function () { return discordAtual; };
  // pra componente React: chama o setter agora e de novo quando o mapa chegar
  window.gzSeguirDiscordUrl = function (setter) {
    setter(discordAtual);
    var h = function () { setter(discordAtual); };
    window.addEventListener('gz:discord-url', h);
    return function () { window.removeEventListener('gz:discord-url', h); };
  };
  (function resolverConvite() {
    // NAO usar window.gzOrigem() aqui: ela so' e' definida no fim deste
    // arquivo, e chamar antes estoura calado — todas as portas ficavam no geral.
    var o = null;
    try { o = JSON.parse(ler(CHAVE) || 'null'); } catch (e) { o = null; }
    var camp = o && o.utm_campaign;
    if (!camp) return;
    fetch('data/campanhas.json').then(function (r) { return r.ok ? r.json() : null; }).then(function (mapa) {
      var url = mapa && mapa.campanhas && mapa.campanhas[String(camp).toLowerCase()];
      // so' aceita convite do Discord: o JSON e' nosso, mas um erro de digitacao
      // la' nao pode virar um link pra qualquer lugar em 4 botoes
      if (url && /^https:\/\/discord\.gg\/[A-Za-z0-9]+$/.test(url)) {
        discordAtual = url;
        window.dispatchEvent(new Event('gz:discord-url'));
      }
    }).catch(function () { /* sem rede: fica o geral */ });
  })();

  // a sessao pode nascer depois (volta do OAuth), entao escuta a troca ALEM de
  // tentar na carga — sem isso a origem so' seria enviada na visita seguinte.
  entregar();
  var tentar = setInterval(entregar, 3000);
  setTimeout(function () { clearInterval(tentar); }, 30000);
  if (window.sb && window.sb.auth && window.sb.auth.onAuthStateChange) {
    window.sb.auth.onAuthStateChange(function () { entregar(); });
  }

  // util pra conferir no console: window.gzOrigem()
  window.gzOrigem = function () {
    try { return JSON.parse(ler(CHAVE) || 'null'); } catch (e) { return null; }
  };
})();
