// Consultas contra o banco do Genius do cliente (MySQL 5.1+).
// Schema real levantado do dump genius.sql (jul/2026) -- ver MEMORIA.md.
//
// A partir da sincronizacao incremental por banco-espelho (`{codigo}_genius`
// no servidor central), as tabelas de fluxo/estado (caixa, vendas, compras,
// itens_receber, contas_receber, itens_pagar, contas_pagar, clientes,
// fornecedores, produtos_estoque, produtos, funcionarios, filiais) sao
// clonadas linha a linha (SELECT * , sem JOIN/alias) -- o JOIN que hoje da
// nome_cliente/nome_fornecedor/etc pronto passa a acontecer no servidor,
// na hora de ler pro painel.

const TABELAS = {
  // caixa tem a coluna DataHoraAlteracao no schema, mas na pratica ela vem
  // sempre NULL (confirmado direto no Genius de um cliente real) -- essa
  // instalacao nao preenche esse campo. Tratada como Tier 2 (por data de
  // criacao + janela de seguranca), igual vendas/compras, em vez de confiar
  // numa coluna que existe no schema mas nao e alimentada de verdade.
  caixa: { colunaCriacao: "data_caixa" },
  vendas: { colunaCriacao: "data_venda" },
  compras: { colunaCriacao: "data_compra" },
  itens_receber: { colunaAlteracao: "data_ocorrencia" },
  contas_receber: { colunaAlteracao: "data_ocorrencia" },
  clientes: { colunaAlteracao: "data_ocorrencia" },
  produtos_estoque: { colunaAlteracao: "data_ocorrencia" },
  produtos: { colunaAlteracao: "data_ocorrencia" },
  funcionarios: { colunaAlteracao: "data_ocorrencia" },
  itens_pagar: {},
  contas_pagar: {},
  fornecedores: {},
  filiais: {},
};

// Tamanho de cada pagina de buscarTabela() -- SELECT * (linha inteira, sem
// alias) em tabela com campo de texto livre (ex: Descricao_caixa) pode passar
// de 600 bytes/linha; um LIMIT fixo alto (testado: 100000) ja estourou 60MB
// numa unica sincronizacao. Por isso nao ha mais teto por tabela -- buscarTabela
// pagina por cursor de Id, e index.js manda uma requisicao por pagina, do
// tamanho que for preciso ate esgotar a tabela (sem cortar dado nenhum).
const TAMANHO_PAGINA = 3000;

// SHOW CREATE TABLE da tabela de origem -- mandado pro servidor SO na 1a vez
// que ele ainda nao tem essa tabela pra esse cliente, pra criar o espelho com
// a estrutura exata do Genius DESSE cliente especifico (pode variar por
// versao/customizacao entre clientes diferentes).
async function capturarSchema(conn, tabela) {
  const [[row]] = await conn.query(`SHOW CREATE TABLE \`${tabela}\``);
  return row["Create Table"];
}

// mysql2 devolve coluna DATE/DATETIME como objeto Date e BLOB como Buffer --
// JSON.stringify (usado no fetch pro servidor) serializa os dois de um jeito
// que quebra o INSERT no espelho (Date vira string ISO com "T"/"Z", que o
// MySQL nao aceita bem num literal de DATETIME; Buffer vira
// {type:"Buffer",data:[...]}, que nao e um valor valido pra coluna BLOB).
// Por isso toda linha passa por aqui antes de ir pro payload: Date vira
// "YYYY-MM-DD HH:MM:SS" (usando os getters locais, nao toISOString, pra nao
// deslocar fuso horario) e Buffer vira base64 com um prefixo que o servidor
// reconhece e converte de volta antes do INSERT (ver inserirLinhasEspelho em
// server.js).
function formatarDataHoraMysql(d) {
  const y = d.getFullYear();
  const mo = String(d.getMonth() + 1).padStart(2, "0");
  const dia = String(d.getDate()).padStart(2, "0");
  const h = String(d.getHours()).padStart(2, "0");
  const mi = String(d.getMinutes()).padStart(2, "0");
  const s = String(d.getSeconds()).padStart(2, "0");
  return `${y}-${mo}-${dia} ${h}:${mi}:${s}`;
}

function serializarLinha(row) {
  const saida = {};
  for (const [k, v] of Object.entries(row)) {
    if (v instanceof Date) saida[k] = formatarDataHoraMysql(v);
    else if (Buffer.isBuffer(v)) saida[k] = "__b64__" + v.toString("base64");
    else saida[k] = v;
  }
  return saida;
}

// Busca UMA PAGINA (ate TAMANHO_PAGINA linhas, "Id > cursorId", ordem
// crescente) de uma tabela, linha completa (sem alias), filtrada por empresa
// e, quando aplicavel, pela data de corte incremental:
// - tabela com coluna de ALTERACAO (DataHoraAlteracao/data_ocorrencia): pega
//   so quem mudou depois de "desde" -- preciso, pega edicao e insercao.
// - tabela so com coluna de CRIACAO (vendas/compras, sem coluna de alteracao
//   no Genius): "desde" ja vem com a janela de seguranca de 3 dias aplicada
//   por quem chama (index.js), pra tambem pegar edicao/cancelamento recente.
// - tabela sem nenhuma das duas (pequena, sem heuristica possivel): sempre
//   busca tudo, "desde" e ignorado.
// Quem chama pagina sozinho: pega o maior Id da pagina recebida, usa como
// proximo cursorId, para quando a pagina vier com menos que TAMANHO_PAGINA
// linhas (ver sincronizarTudo em index.js).
async function buscarTabela(conn, tabela, idEmpresa, desde, cursorId = 0) {
  const cfg = TABELAS[tabela];
  if (!cfg) throw new Error(`Tabela desconhecida: ${tabela}`);
  const coluna = cfg.colunaAlteracao || cfg.colunaCriacao;
  const condicoes = ["id_empresa = ?", "Id > ?"];
  const params = [idEmpresa, cursorId];
  if (desde && coluna) {
    condicoes.push(`\`${coluna}\` >= ?`);
    params.push(desde);
  }
  const [rows] = await conn.query(
    `SELECT * FROM \`${tabela}\` WHERE ${condicoes.join(" AND ")} ORDER BY Id ASC LIMIT ${TAMANHO_PAGINA}`,
    params
  );
  return rows.map(serializarLinha);
}

// Lista de empresas deste cliente. Cliente com varias empresas no mesmo banco
// (ex: rede com varias lojas) tem uma linha por empresa em "filiais". Cliente
// com 1 empresa so costuma deixar "filiais" vazia -- nesse caso assume so a
// empresa 1 (mesmo comportamento de antes, mantem retrocompatibilidade).
async function buscarEmpresas(conn) {
  const [rows] = await conn.query(
    "SELECT DISTINCT id_empresa, nome_filial AS nome FROM filiais ORDER BY id_empresa"
  );
  if (rows.length === 0) return [{ id_empresa: 1, nome: null }];
  return rows;
}

// Credenciais dos funcionarios (login) para o painel mobile poder validar login
// sem precisar do agente online no momento do acesso. IMPORTANTE: a senha do
// Genius (Senha_fun) esta em texto puro no banco dele (varchar curto, sistema
// antigo) -- aqui SEMPRE fazemos o hash antes de mandar pro servidor central
// para o FLUXO DE LOGIN (tabela credenciais). Isso e independente da copia
// espelho da tabela "funcionarios" (que replica Senha_fun em texto puro por
// decisao explicita -- ver MEMORIA.md); o login nunca usa a copia espelho.
async function buscarCredenciais(conn, bcrypt) {
  // NAO filtramos por "usado" aqui de proposito -- nao confirmamos o
  // significado exato dessa coluna no schema (poderia excluir funcionario
  // ativo por engano). Se precisar excluir inativos, confirmar a semantica
  // certa antes de adicionar o filtro.
  const [rows] = await conn.query(
    `SELECT Login_fun AS login, Senha_fun AS senha, Nome_fun AS nome, id_empresa
     FROM funcionarios
     WHERE Login_fun IS NOT NULL AND Login_fun <> ''`
  );
  return rows
    .filter((r) => r.login && r.senha)
    .map((r) => ({
      login: r.login,
      senha_hash: bcrypt.hashSync(r.senha, 10),
      nome_funcionario: r.nome,
      id_empresa: r.id_empresa,
    }));
}

// Dados pra auto-registro no servidor central: codigo vem de
// configuracoes.codigo_cliente (sempre preenchido, e o mesmo codigo usado
// pra licenciar o Genius). nome vem de parametros.Fantasia_empre (nome
// fantasia da empresa -- tabela unica por instalacao, nao tem id_empresa,
// confirmado testando com o banco real da GREEN FIT), caindo pra
// parametros.Nome_empre (razao social) se fantasia estiver vazio, e so
// como ultimo recurso filiais.nome_filial (so usado em multi-filial,
// costuma estar vazio em instalacao simples). Se nada disso existir, nome
// fica null e o admin preenche na hora de aprovar.
async function buscarDadosRegistro(conn, idEmpresa) {
  const [config] = await conn.query("SELECT codigo_cliente FROM configuracoes LIMIT 1");
  const codigo = config[0] ? String(config[0].codigo_cliente || "").trim() : "";
  if (!codigo) return null;

  const [parametros] = await conn.query(
    "SELECT Fantasia_empre, Nome_empre FROM parametros LIMIT 1"
  );
  let nome = parametros[0] ? (parametros[0].Fantasia_empre || parametros[0].Nome_empre) : null;

  if (!nome) {
    const [filial] = await conn.query(
      "SELECT nome_filial FROM filiais WHERE id_empresa=? LIMIT 1",
      [idEmpresa]
    );
    nome = filial[0] ? filial[0].nome_filial : null;
  }

  return { codigo, nome, id_empresa: idEmpresa };
}

module.exports = {
  TABELAS,
  TAMANHO_PAGINA,
  capturarSchema,
  buscarTabela,
  buscarEmpresas,
  buscarCredenciais,
  buscarDadosRegistro,
};
