// =============================================================================
// COMPRA EM 2 TEMPOS — "é pra você ou é presente?" + recibo com instrução
// =============================================================================
// ANTES: clicou em Comprar, o Z Buck sumiu e apareceu uma faixinha de texto no
// topo da página. Quem estava lá embaixo, no card, não via a faixa — e ninguém
// aprendia onde resgatar.
//
// AGORA, todo botão de compra da Z Shop passa por aqui:
//   1. DESTINO   "Esse item é para sua conta?"  SIM | enviar como presente
//   2. QUEM      (só no presente) busca o jogador pelo nome, estilo Steam
//   3. RECIBO    modal central, fundo escurecido, com a instrução DA CATEGORIA
//
// 🔑 A instrução é uma TABELA, não um `if` espalhado pela tela. Cada categoria
// chega no jogo por um caminho diferente, e o texto tem que dizer o caminho
// certo — foi assim que o site passou meses mandando o jogador no lugar errado
// (ver INSTRUCOES.item abaixo).
// =============================================================================

// --- para onde vai cada coisa, e o que o jogador faz pra receber --------------
// 🚨 `item` e `emote` dizem CORREIO, não Resgate. Desde a migration
// 20260715140000 a ponte só escreve CAR no arquivo do Resgate; item vai por
// carta. O toast antigo mandava todo mundo pro "F5 > aba RESGATE".
const GZ_INSTRUCOES = {
  carro: {
    titulo: 'Seu veículo está a caminho',
    passos: [
      'Entre no servidor e aperte <b>F5 → aba VEÍCULOS</b>.',
      'Resgate <b>em espaço aberto</b>: o carro nasce do seu lado, no seu nome e já com a chave.',
    ],
    obs: 'Pode levar até ~1 minuto pra aparecer na lista.',
  },
  animal: {
    titulo: 'Seu bicho está a caminho',
    passos: [
      'Ele chega dentro de uma <b>cápsula GenZ lacrada</b>, pelo <b>Correio</b> (F5 → Correio).',
      'Colete a carta e abra a cápsula onde você quiser — nasce já reivindicado no seu nome.',
    ],
    obs: 'Sem precisar domar. Ninguém abate o seu bicho.',
  },
  emote: {
    titulo: 'Sua dança está a caminho',
    passos: [
      'A <b>revista</b> chega pelo <b>Correio</b> (F5 → Correio).',
      'Colete a carta e <b>leia a revista pra aprender</b> — depois a dança fica no seu menu de emotes.',
    ],
    obs: 'Toca uma de 3 músicas, sorteada a cada vez que você dança.',
  },
  item: {
    titulo: 'Seu item está a caminho',
    passos: [
      'Chega pelo <b>Correio</b> do jogo (F5 → Correio).',
      'Colete a carta pra receber no inventário.',
    ],
    obs: 'Pode levar até ~1 minuto.',
  },
  conta: {
    titulo: 'Já está ativo na sua conta',
    passos: [
      'Não é item: vale em <b>qualquer personagem</b> seu, com a Steam vinculada (aba CONTA).',
    ],
    obs: 'O jogo pode levar até ~1 minuto pra enxergar a mudança.',
  },
};

// categoria do produto -> chave da tabela acima
function gzInstrucaoDe(categoria) {
  if (categoria === 'carros' || categoria === 'carro') return GZ_INSTRUCOES.carro;
  if (categoria === 'animal') return GZ_INSTRUCOES.animal;
  if (categoria === 'emote') return GZ_INSTRUCOES.emote;
  if (categoria === 'vip' || categoria === 'slot' || categoria === 'safehouse') return GZ_INSTRUCOES.conta;
  return GZ_INSTRUCOES.item;
}

// --- validade por Season (Regra das Seasons, 2026-08-25) ----------------------
// O servidor roda em Seasons com wipe. A regra que decide o selo:
//   gameplay  -> vale a Season (morre no wipe; últimos 30 dias re-entrega)
//   cosmético -> da conta, atravessa as Seasons (hoje: emote)
//   vip       -> 30 dias corridos, independente de Season
// Fonte: marketing/docs/plano-seasons-e-legado.md + cláusula na política de donate.
const GZ_VALIDADE = {
  season: {
    selo: '⏳ VALE A SEASON', cor: '#ffb020',
    frase: 'Vale até o fim da Season atual. Adquirido nos últimos 30 dias da Season? É re-entregue na próxima.',
  },
  sempre: {
    selo: '♾️ SEU PRA SEMPRE', cor: '#a8ff00',
    frase: 'Fica na sua conta e atravessa as Seasons.',
  },
  mensal: {
    selo: '📅 VALE 30 DIAS', cor: '#f5c542',
    frase: 'Vale 30 dias a partir da ativação, independente de Season.',
  },
};
function gzValidadeDe(categoria) {
  if (categoria === 'emote') return GZ_VALIDADE.sempre;
  if (categoria === 'vip') return GZ_VALIDADE.mensal;
  return GZ_VALIDADE.season; // carros, animal, itens (Caixa), slot, safehouse...
}
// Chip pequeno, usado nos cards das vitrines e aqui no modal. Lazy via window
// nos outros arquivos (ordem de script não importa: só é lido no render).
function GzSeloValidade({ categoria, style }) {
  const v = gzValidadeDe(categoria);
  return (
    <span title={v.frase} style={Object.assign({
      display: 'inline-flex', alignItems: 'center',
      fontFamily: 'Share Tech Mono', fontSize: 10, letterSpacing: '.08em',
      textTransform: 'uppercase', whiteSpace: 'nowrap',
      color: v.cor, border: '1px solid ' + v.cor + '55',
      background: v.cor + '14', padding: '3px 8px',
    }, style || {})}>{v.selo}</span>
  );
}

function gzFmtZb(n) { return Number(n || 0).toLocaleString('pt-BR'); }

// --- CSS injetado 1x ----------------------------------------------------------
function ensureCompraCss() {
  if (document.getElementById('gz-compra-css')) return;
  const st = document.createElement('style');
  st.id = 'gz-compra-css';
  st.textContent = `
    /* 🪤 nome de classe com prefixo proprio: ".modal" cru e' morto por
       ad-blocker (ja' aconteceu no projeto). */
    .gz-cmp-fundo { position: fixed; inset: 0; z-index: 300; display: flex;
      align-items: center; justify-content: center; padding: 20px;
      background: rgba(6,6,6,.82); backdrop-filter: blur(4px);
      animation: gzCmpFundo .18s ease; }
    @keyframes gzCmpFundo { from { opacity: 0; } }
    .gz-cmp-caixa { width: 100%; max-width: 520px; max-height: calc(100vh - 40px);
      overflow-y: auto; background: #171717; border: 1px solid rgba(255,255,255,.1);
      border-top: 3px solid #a8ff00; padding: 26px 26px 24px;
      box-shadow: 0 30px 80px rgba(0,0,0,.7); animation: gzCmpEntra .22s cubic-bezier(.2,.7,.3,1);
      clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 16px 100%, 0 calc(100% - 16px)); }
    @keyframes gzCmpEntra { from { opacity: 0; transform: translateY(14px) scale(.98); } }
    .gz-cmp-eyebrow { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
    .gz-cmp-eyebrow i { width: 22px; height: 2px; background: #a8ff00; display: block; }
    .gz-cmp-eyebrow span { font-family: 'Share Tech Mono', monospace; font-size: 10.5px;
      color: #a8ff00; letter-spacing: .18em; text-transform: uppercase; }
    .gz-cmp-titulo { font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 27px;
      text-transform: uppercase; line-height: 1.05; text-wrap: balance; }
    .gz-cmp-bt { width: 100%; border: none; cursor: pointer; padding: 14px 18px;
      font-family: 'Rajdhani', sans-serif; font-weight: 700; font-size: 14.5px;
      letter-spacing: .1em; text-transform: uppercase; transition: filter .15s;
      clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px)); }
    .gz-cmp-bt:hover:not(:disabled) { filter: brightness(1.08); }
    .gz-cmp-bt:disabled { cursor: not-allowed; }
    .gz-cmp-bt.sim { background: #a8ff00; color: #111; }
    .gz-cmp-bt.presente { background: transparent; color: #f5c542; border: 1px solid rgba(245,197,66,.5); }
    .gz-cmp-bt.presente:hover { background: rgba(245,197,66,.1); }
    .gz-cmp-bt.fraco { background: rgba(255,255,255,.06); color: rgba(255,255,255,.42); }
    .gz-cmp-voltar { background: none; border: none; cursor: pointer; padding: 4px 0;
      font-family: 'Share Tech Mono', monospace; font-size: 11px; letter-spacing: .1em;
      color: rgba(255,255,255,.42); text-transform: uppercase; }
    .gz-cmp-voltar:hover { color: rgba(255,255,255,.75); }
    .gz-cmp-campo { width: 100%; background: #0e0e0e; border: 1px solid rgba(255,255,255,.16);
      color: #fff; font-family: 'Share Tech Mono', monospace; font-size: 15px;
      padding: 12px 14px; outline: none; }
    .gz-cmp-campo:focus { border-color: #a8ff00; }
    .gz-cmp-lista { list-style: none; margin: 8px 0 0; padding: 0; max-height: 208px; overflow-y: auto;
      border: 1px solid rgba(255,255,255,.08); }
    .gz-cmp-lista li button { width: 100%; text-align: left; background: transparent; border: none;
      color: rgba(255,255,255,.82); font-family: 'Share Tech Mono', monospace; font-size: 14px;
      padding: 10px 14px; cursor: pointer; display: flex; align-items: center; gap: 10px; }
    .gz-cmp-lista li button:hover, .gz-cmp-lista li button.on { background: rgba(168,255,0,.1); color: #fff; }
    .gz-cmp-lista li button.on::after { content: '✓'; margin-left: auto; color: #a8ff00; }
    .gz-cmp-lista li + li { border-top: 1px solid rgba(255,255,255,.06); }
    .gz-cmp-passos { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; }
    .gz-cmp-passos li { display: flex; gap: 11px; font-size: 13.5px; line-height: 1.6; color: rgba(255,255,255,.78); }
    .gz-cmp-passos li b { color: #fff; font-weight: 700; }
    .gz-cmp-passos .n { flex-shrink: 0; width: 21px; height: 21px; margin-top: 1px;
      display: flex; align-items: center; justify-content: center;
      background: rgba(168,255,0,.14); border: 1px solid rgba(168,255,0,.4); color: #a8ff00;
      font-family: 'Share Tech Mono', monospace; font-size: 11px; }
    .gz-cmp-extrato { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
      font-family: 'Share Tech Mono', monospace; font-size: 12.5px; color: rgba(255,255,255,.5);
      padding: 11px 0; border-top: 1px solid rgba(255,255,255,.08); }
    .gz-cmp-extrato b { color: #a8ff00; font-weight: 400; font-size: 14px; }
    .gz-cmp-erro { border: 1px solid rgba(255,90,90,.42); background: rgba(255,90,90,.08);
      color: #ff8a8a; font-size: 13px; padding: 11px 14px; line-height: 1.5; }
    @media (max-width: 560px) { .gz-cmp-caixa { padding: 20px 18px 18px; } .gz-cmp-titulo { font-size: 22px; } }
  `;
  document.head.appendChild(st);
}

// --- busca de jogador com atraso ---------------------------------------------
// 250 ms de espera antes de perguntar: sem isso "wolfmach" vira 8 chamadas.
function useBuscaJogador(termo, ligado) {
  const { useState, useEffect } = React;
  const [nomes, setNomes] = useState([]);
  const [buscando, setBuscando] = useState(false);
  useEffect(() => {
    if (!ligado) return;
    const q = (termo || '').trim();
    if (q.length < 2) { setNomes([]); setBuscando(false); return; }
    if (!window.gzBuscarJogador) { setNomes([]); return; }
    let vivo = true;
    setBuscando(true);
    const t = setTimeout(() => {
      window.gzBuscarJogador(q).then(r => {
        if (!vivo) return;
        // A RPC `genz_buscar_jogador` e' `RETURNS TABLE(nome TEXT)`, entao o
        // PostgREST devolve [{nome:'...'}] -- nao string solta, nao game_username.
        // 🚨 Ler o campo errado nao dava lista vazia: caia no `|| x` e mandava o
        // OBJETO pro JSX, o que derruba a arvore inteira do React (tela cinza no
        // meio da compra, 24/08). So' string entra na lista, aconteca o que
        // acontecer com a forma do retorno.
        setNomes((r || [])
          .map(x => (typeof x === 'string' ? x : (x && (x.nome || x.game_username)) || ''))
          .filter(n => typeof n === 'string' && n.length > 0));
        setBuscando(false);
      }).catch(() => { if (vivo) { setNomes([]); setBuscando(false); } });
    }, 250);
    return () => { vivo = false; clearTimeout(t); };
  }, [termo, ligado]);
  return { nomes, buscando };
}

// --- o modal ------------------------------------------------------------------
// `pedido` = { produto, categoria, extra, comprar(destinatario) -> Promise }
// `comprar` devolve o mesmo objeto que as RPCs devolvem ({success, balance, ...}).
function GzCompraModal({ pedido, onFechar }) {
  const { useState, useEffect, useRef } = React;
  // 🪤 tela e "esta' cobrando" sao coisas SEPARADAS. Quando eram a mesma
  // variavel, apertar "Enviar pra Fulano" trocava a fase pra 'processando', a
  // tela do presente sumia e a de destino piscava no lugar — no meio da cobranca.
  const [tela, setTela] = useState('destino');   // destino | quem | recibo
  const [ocupado, setOcupado] = useState(false);
  const [termo, setTermo] = useState('');
  const [escolhido, setEscolhido] = useState(null);
  const [resultado, setResultado] = useState(null);
  const [erro, setErro] = useState(null);
  const campoRef = useRef(null);
  const { nomes, buscando } = useBuscaJogador(termo, tela === 'quem');

  useEffect(() => { ensureCompraCss(); }, []);

  // ESC fecha — menos no meio da cobrança, senão o jogador some com a tela
  // enquanto o dinheiro está saindo e não vê onde resgatar.
  useEffect(() => {
    function tecla(e) {
      if (e.key === 'Escape' && !ocupado) onFechar();
    }
    window.addEventListener('keydown', tecla);
    return () => window.removeEventListener('keydown', tecla);
  }, [ocupado, onFechar]);

  useEffect(() => { if (tela === 'quem' && campoRef.current) campoRef.current.focus(); }, [tela]);

  if (!pedido) return null;
  const p = pedido.produto || {};
  const total = (p.price_credits || 0) + (pedido.extra || 0);
  const instr = gzInstrucaoDe(pedido.categoria || p.category);
  const destinatario = escolhido || (termo.trim() || null);

  function confirmar(paraQuem) {
    setErro(null);
    setOcupado(true);
    Promise.resolve(pedido.comprar(paraQuem || null)).then(r => {
      setOcupado(false);
      if (!r || r.success === false) {
        const m = r && r.error === 'insufficient_funds' ? 'Z Bucks insuficientes pra essa compra.'
          : r && r.error === 'recipient_not_found' ? 'Não achei ninguém com esse nome no servidor. Confira a grafia.'
          : 'Não consegui concluir a compra. Tente de novo em instantes.';
        setErro(m);
        return;   // fica na MESMA tela, com o erro em cima do botão que falhou
      }
      setResultado({ ...r, para: paraQuem || null });
      setTela('recibo');
    }).catch(() => {
      setOcupado(false);
      setErro('Não consegui concluir a compra. Tente de novo em instantes.');
    });
  }

  const parar = e => e.stopPropagation();

  return (
    <div className="gz-cmp-fundo" onClick={() => !ocupado && onFechar()}>
      <div className="gz-cmp-caixa" onClick={parar} role="dialog" aria-modal="true">

        {/* ---------------------------------------------------- 1. DESTINO */}
        {tela === 'destino' && (
          <React.Fragment>
            <div className="gz-cmp-eyebrow"><i /><span>Confirmar compra</span></div>
            <div className="gz-cmp-titulo">{p.title}</div>
            <div className="gz-cmp-extrato" style={{ marginTop: 14 }}>
              <span>{pedido.extra ? 'ITEM + EXTRAS' : 'TOTAL'}</span>
              <b>{gzFmtZb(total)} ZB</b>
            </div>
            {/* validade ANTES do botão: a regra vive onde o dedo clica */}
            <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, marginTop: 12, flexWrap: 'wrap' }}>
              <GzSeloValidade categoria={pedido.categoria || p.category} style={{ marginTop: 1 }} />
              <span style={{ flex: '1 1 220px', fontSize: 12, lineHeight: 1.55, color: 'rgba(255,255,255,.55)' }}>
                {gzValidadeDe(pedido.categoria || p.category).frase}
              </span>
            </div>
            <p style={{ color: 'rgba(255,255,255,.6)', fontSize: 13.5, lineHeight: 1.6, margin: '14px 0 18px' }}>
              Esse item é para a sua conta?
            </p>
            {erro && <div className="gz-cmp-erro" style={{ marginBottom: 14 }}>{erro}</div>}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <button className="gz-cmp-bt sim gzb" disabled={ocupado} onClick={() => confirmar(null)}>
                {ocupado ? 'Processando...' : 'Sim, é pra mim'}
              </button>
              <button className="gz-cmp-bt presente gzb" disabled={ocupado}
                onClick={() => { setErro(null); setTela('quem'); }}>
                Não, quero dar de presente
              </button>
            </div>
            <div style={{ marginTop: 16, textAlign: 'center' }}>
              <button className="gz-cmp-voltar gzb" disabled={ocupado} onClick={onFechar}>cancelar</button>
            </div>
          </React.Fragment>
        )}

        {/* ------------------------------------------------------- 2. QUEM */}
        {tela === 'quem' && (
          <React.Fragment>
            <div className="gz-cmp-eyebrow"><i /><span>Presente</span></div>
            <div className="gz-cmp-titulo">Pra quem?</div>
            <p style={{ color: 'rgba(255,255,255,.6)', fontSize: 13.5, lineHeight: 1.6, margin: '12px 0 16px' }}>
              Digite o nome do personagem <b style={{ color: '#fff' }}>dentro do jogo</b>. A compra
              sai do seu saldo e a entrega vai pra conta dele.
            </p>
            <input ref={campoRef} className="gz-cmp-campo" value={termo} placeholder="nome no jogo"
              autoComplete="off" spellCheck="false"
              onChange={e => { setTermo(e.target.value); setEscolhido(null); }} />
            {termo.trim().length >= 2 && (
              <ul className="gz-cmp-lista">
                {buscando && <li><button className="gzb" disabled style={{ color: 'rgba(255,255,255,.35)' }}>procurando...</button></li>}
                {!buscando && nomes.length === 0 && (
                  <li><button className="gzb" disabled style={{ color: 'rgba(255,255,255,.35)' }}>
                    ninguém com esse nome — dá pra mandar assim mesmo, mas confira a grafia
                  </button></li>
                )}
                {nomes.map(n => (
                  <li key={n}>
                    <button className={'gzb ' + (escolhido === n ? 'on' : '')}
                      onClick={() => { setEscolhido(n); setTermo(n); }}>{n}</button>
                  </li>
                ))}
              </ul>
            )}
            {erro && <div className="gz-cmp-erro" style={{ marginTop: 14 }}>{erro}</div>}
            <div className="gz-cmp-extrato" style={{ marginTop: 16 }}>
              <span>SAI DO SEU SALDO</span><b>{gzFmtZb(total)} ZB</b>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 8 }}>
              <button className={'gzb gz-cmp-bt ' + (destinatario ? 'sim' : 'fraco')}
                disabled={!destinatario || ocupado}
                onClick={() => confirmar(destinatario)}>
                {ocupado ? 'Enviando...'
                  : destinatario ? ('Enviar pra ' + destinatario) : 'Escolha quem recebe'}
              </button>
            </div>
            <div style={{ marginTop: 16, textAlign: 'center' }}>
              <button className="gz-cmp-voltar gzb" disabled={ocupado}
                onClick={() => { setErro(null); setTela('destino'); }}>voltar</button>
            </div>
          </React.Fragment>
        )}

        {/* ----------------------------------------------------- 3. RECIBO */}
        {tela === 'recibo' && resultado && (
          <React.Fragment>
            <div className="gz-cmp-eyebrow"><i /><span>Obrigado pela sua compra</span></div>
            <div className="gz-cmp-titulo">
              {resultado.para ? 'Presente enviado' : instr.titulo}
            </div>

            {resultado.para ? (
              <React.Fragment>
                <p style={{ color: 'rgba(255,255,255,.72)', fontSize: 14, lineHeight: 1.65, margin: '14px 0 16px' }}>
                  <b style={{ color: '#f5c542' }}>{p.title}</b> foi enviado pra{' '}
                  <b style={{ color: '#fff' }}>{resultado.para}</b>. Quem resgata é ele — e é isto
                  que ele precisa fazer:
                </p>
                <ol className="gz-cmp-passos">
                  {instr.passos.map((t, i) => (
                    <li key={i}><span className="n">{i + 1}</span>
                      <span dangerouslySetInnerHTML={{ __html: t }} /></li>
                  ))}
                </ol>
              </React.Fragment>
            ) : (
              <ol className="gz-cmp-passos" style={{ marginTop: 16 }}>
                {instr.passos.map((t, i) => (
                  <li key={i}><span className="n">{i + 1}</span>
                    <span dangerouslySetInnerHTML={{ __html: t }} /></li>
                ))}
              </ol>
            )}

            <p style={{ color: 'rgba(255,255,255,.45)', fontSize: 12, lineHeight: 1.6, margin: '14px 0 4px' }}>
              {instr.obs}
            </p>

            <div className="gz-cmp-extrato" style={{ marginTop: 10 }}>
              <span>SALDO AGORA</span>
              <b>{gzFmtZb(typeof resultado.balance === 'number' ? resultado.balance : 0)} ZB</b>
            </div>

            <button className="gz-cmp-bt sim gzb" style={{ marginTop: 12 }} onClick={onFechar}>Fechar</button>
          </React.Fragment>
        )}

      </div>
    </div>
  );
}

Object.assign(window, { GzCompraModal, GZ_INSTRUCOES, gzInstrucaoDe, GZ_VALIDADE, gzValidadeDe, GzSeloValidade });
