/* ===== Ligação com o banco (Supabase) — 14/11 =======================
   Primeira vez que este app fala com um banco de verdade. Até aqui
   tudo vinha de dados de demonstração guardados no navegador.

   POR QUE ESTE ARQUIVO EXISTE
   A tabela `mapas` do banco da Iunes guarda a Equipe e os Processos
   do cliente, e o Hub da ValorIA sincroniza com ela. O app precisa
   LER essa tabela, e quem for master ou admin precisa poder EDITAR.

   COMO A SEGURANÇA FUNCIONA (conferido no banco em 14/11)
   - Todas as regras da tabela valem para o papel `authenticated`.
     Sem login de verdade, o app não lê UMA LINHA — e sem erro: volta
     lista vazia. Por isso o login teve que vir junto.
   - Ler: a função `autorizado()` confere se o e-mail logado está na
     lista `emails_autorizados`.
   - Editar: a função `pode_editar()` exige papel master ou admin.
     Ela é executável pelo usuário logado, então o app pergunta ao
     próprio banco se pode editar, em vez de adivinhar.

   A CHAVE PÚBLICA ABAIXO
   É a chave "publishable" do Supabase. Ela é feita para ficar no
   navegador: sozinha não dá acesso a nada, porque toda leitura e
   escrita passa pelas regras acima. Chave de servidor NUNCA entra aqui.

   A SESSÃO
   Fica em sessionStorage: some quando a aba fecha, sobrevive a um
   recarregar (o seletor Simulada/Real recarrega a página). O token
   expira em cerca de uma hora; quando isso acontece o app pede login
   de novo em vez de fingir que salvou.

   ⚠️ Os DADOS da tabela não são copiados para o navegador: a cada
   abertura de tela o app lê do banco. Isso é regra dele.
   ================================================================== */

const BANCO_URL = 'https://cnnlkdmjueyffedioobp.supabase.co';
const BANCO_KEY = 'sb_publishable_C8M3kwbj3yW_8JpkumBSDg_YE_yEXfP';
const BANCO_SESSAO = 'iunes:sessao';

function bancoLerSessao(){
  try{ const s = sessionStorage.getItem(BANCO_SESSAO); return s ? JSON.parse(s) : null; }
  catch(e){ return null; }
}
function bancoGravarSessao(s){
  try{ if(s) sessionStorage.setItem(BANCO_SESSAO, JSON.stringify(s));
       else sessionStorage.removeItem(BANCO_SESSAO); }catch(e){}
}

/* erro com mensagem em português, para a tela nunca mostrar texto cru */
function bancoErro(resp, texto){
  if(resp.status === 400 || resp.status === 401)
    return new Error('E-mail ou senha não conferem, ou a sessão expirou. Entre de novo.');
  if(resp.status === 403)
    return new Error('Seu acesso não permite esta ação.');
  return new Error('O banco respondeu com erro ' + resp.status + (texto ? ': ' + texto.slice(0,120) : '.'));
}

async function bancoEntrar(email, senha){
  const r = await fetch(BANCO_URL + '/auth/v1/token?grant_type=password', {
    method:'POST',
    headers:{'apikey':BANCO_KEY, 'Content-Type':'application/json'},
    body: JSON.stringify({email: String(email||'').trim(), password: String(senha||'')}),
  });
  const txt = await r.text();
  if(!r.ok) throw bancoErro(r, txt);
  const d = JSON.parse(txt);
  /* senhaPropria: marca gravada na conta quando a pessoa trocou a senha
     que você entregou por uma dela. Enquanto for falsa, o app não deixa
     passar da tela de criar senha. Fica no user_metadata da conta, não
     numa tabela nossa: assim vale em qualquer navegador e ninguém perde
     a marca trocando de computador. */
  const meta = (d.user && d.user.user_metadata) || {};
  const sessao = {token: d.access_token, email: (d.user && d.user.email) || email,
                  senhaPropria: meta.senha_propria === true, em: Date.now()};
  bancoGravarSessao(sessao);
  return sessao;
}

function bancoSair(){ bancoGravarSessao(null); }

/* chamada autenticada; qualquer 401 derruba a sessão em vez de insistir */
async function bancoChamar(caminho, opcoes){
  const s = bancoLerSessao();
  if(!s) throw new Error('Entre com a sua conta para ver os mapas.');
  const o = opcoes || {};
  const r = await fetch(BANCO_URL + caminho, {
    method: o.metodo || 'GET',
    headers: Object.assign({
      'apikey': BANCO_KEY,
      'Authorization': 'Bearer ' + s.token,
      'Content-Type': 'application/json',
    }, o.extra || {}),
    body: o.corpo ? JSON.stringify(o.corpo) : undefined,
  });
  const txt = await r.text();
  if(r.status === 401){ bancoSair(); throw bancoErro(r, txt); }
  if(!r.ok) throw bancoErro(r, txt);
  return txt ? JSON.parse(txt) : null;
}

/* Troca a senha e carimba a marca, numa chamada só: se gravasse a marca
   separado e essa segunda chamada falhasse, a pessoa ficaria com a senha
   nova e sendo perguntada para sempre. */
async function bancoDefinirSenha(nova){
  await bancoChamar('/auth/v1/user', {metodo:'PUT',
    corpo:{password: nova, data:{senha_propria:true}}});
  const s = bancoLerSessao();
  if(s){ s.senhaPropria = true; bancoGravarSessao(s); }
}

/* pergunta ao banco se este usuário pode editar (master ou admin) */
async function bancoPodeEditar(){
  try{ const r = await bancoChamar('/rest/v1/rpc/pode_editar', {metodo:'POST', corpo:{}}); return r === true; }
  catch(e){ return false; }
}

/* Quem é a pessoa que entrou. A tabela emails_autorizados é fechada para
   leitura (RLS sem política nenhuma), então o app NÃO consulta a tabela:
   pergunta ao banco por esta função, que só devolve a linha dela.
   Devolve null quando o e-mail não está na lista — e aí o app recusa a
   entrada em vez de deixar passar sem função. */
async function bancoMeuAcesso(){
  const r = await bancoChamar('/rest/v1/rpc/meu_acesso', {metodo:'POST', corpo:{}});
  return (Array.isArray(r) && r.length) ? r[0] : null;
}

const bancoMapas = (tipo) =>
  bancoChamar('/rest/v1/mapas?select=*&tipo=eq.' + encodeURIComponent(tipo) + '&order=ordem.asc');

/* Grava a alteração E carimba a data, como o Hub exige: é por ela que
   ele decide qual versão é a mais nova na sincronização. */
const bancoSalvarMapa = (id, dados) =>
  bancoChamar('/rest/v1/mapas?id=eq.' + encodeURIComponent(id), {
    metodo:'PATCH',
    extra:{'Prefer':'return=representation'},
    corpo: Object.assign({}, dados, {atualizado: Date.now(), atualizado_por: 'app'}),
  });

const bancoHistorico = (id) =>
  bancoChamar('/rest/v1/mapas_historico?select=*&mapa_id=eq.' + encodeURIComponent(id) + '&order=em.desc&limit=20');

/* ===== Frota: manutenção e consumo no banco (14/11) =================
   Decisão dele: "Visualização Real = banco, exige conta". Então:

     SIMULADA  → dados de demonstração no navegador, como sempre.
                 Serve para mostrar, não guarda nada de verdade.
     REAL      → tabelas frota_manutencao e frota_consumo no Supabase.
                 Exige login. O que for lançado aparece em qualquer
                 computador, que era o problema.

   As duas tabelas foram criadas em 14/11. Nenhuma tabela que já
   existia foi tocada. A regra de acesso delas é de propósito mais
   solta do que a de `mapas`:
     ler e lançar     → qualquer e-mail autorizado
     corrigir/apagar  → só master ou admin
   Porque quem cuida da frota precisa lançar todo dia, mas mexer em
   histórico já gravado é decisão de quem manda.

   A data vai para o banco como data de verdade (não texto), senão
   não dá para somar km por semana nem por mês no painel. */

/* dd/mm/aaaa (como as telas escrevem) <-> aaaa-mm-dd (como o banco guarda) */
function bancoDataIso(br){
  const p = String(br||'').trim().split('/');
  if(p.length !== 3) return null;
  const d = p[0].padStart(2,'0'), m = p[1].padStart(2,'0'), a = p[2];
  if(a.length !== 4) return null;
  return a + '-' + m + '-' + d;
}
function bancoDataBr(iso){
  const p = String(iso||'').slice(0,10).split('-');
  return p.length === 3 ? (p[2] + '/' + p[1] + '/' + p[0]) : String(iso||'');
}

const bancoFrotaLer = (tabela) =>
  bancoChamar('/rest/v1/' + tabela + '?select=*&order=data.desc');

function bancoFrotaInserir(tabela, linha){
  const agora = Date.now();
  return bancoChamar('/rest/v1/' + tabela, {
    metodo:'POST',
    extra:{'Prefer':'return=representation'},
    corpo: Object.assign({}, linha, {criado: agora, atualizado: agora, atualizado_por: 'app'}),
  });
}

/* Decide de onde vem o dado da tela. Na Simulada nunca encosta no
   banco; na Real nunca encosta no navegador. Sem meio-termo, para não
   existir tela mostrando metade de cada lugar. */
function useFrotaDados(tabela, chaveLocal, semente, paraBanco, doBanco){
  const noBanco = REAL;
  /* Na Real nem a cópia local é criada: a regra é não existir tela
     mostrando metade de cada lugar, e não sujar o navegador com uma
     gaveta vazia que ninguém lê. */
  const [local,setLocal] = useState(()=>{
    if(noBanco) return [];
    try{ const g = localStorage.getItem(STORE+chaveLocal); return g ? JSON.parse(g) : seed(semente); }
    catch(e){ return seed(semente); }
  });
  useEffect(()=>{ if(noBanco) return;
    try{ localStorage.setItem(STORE+chaveLocal, JSON.stringify(local)); }catch(e){}
  },[chaveLocal, local, noBanco]);
  const [remoto,setRemoto] = useState(null);
  const [erro,setErro] = useState('');
  const [recarga,setRecarga] = useState(0);

  useEffect(()=>{
    if(!noBanco) return;
    let vivo = true;
    setErro('');
    if(!bancoLerSessao()){ setRemoto([]); setErro('conta'); return; }
    setRemoto(null);
    bancoFrotaLer(tabela)
      .then(d=>{ if(vivo) setRemoto((d||[]).map(doBanco)); })
      .catch(e=>{ if(vivo){ setRemoto([]); setErro(e.message || 'Não deu para ler o banco.'); } });
    return ()=>{ vivo = false; };
  },[tabela, noBanco, recarga]);

  const inserir = async (linha)=>{
    if(!noBanco){ setLocal(c=>[linha, ...c]); return {ok:true, onde:'navegador'}; }
    try{ await bancoFrotaInserir(tabela, paraBanco(linha)); setRecarga(n=>n+1); return {ok:true, onde:'banco'}; }
    catch(e){ return {ok:false, erro: e.message || 'Não deu para gravar no banco.'}; }
  };

  return {
    linhas: noBanco ? (remoto || []) : local,
    carregando: noBanco && remoto === null && !erro,
    erro, inserir, noBanco,
    precisaConta: erro === 'conta',
  };
}
