const { Client } = require('pg'); 
const mysql = require('mysql2/promise');
const dotenv = require('dotenv');
const fs = require('fs');
const moment = require('moment');

dotenv.config();

const envData = fs.readFileSync('conecta.env', 'utf8');
const envVariables = dotenv.parse(envData);

function printProgressBar(completed, total, barLength = 40) {
  const progress = completed / total;
  const filledLength = Math.round(barLength * progress);
  const emptyLength = barLength - filledLength;

  const bar = '█'.repeat(filledLength) + '░'.repeat(emptyLength);
  const percent = (progress * 100).toFixed(1).padStart(5, ' ');
  const status = `${completed}/${total}`.padEnd(15, ' ');
  const now = new Date();
  const time = now.toLocaleTimeString('pt-BR');

  const output = `\rG-SAUDE Processando DB e-SUS, Aguarde! ${bar} ${percent}% (${status}) -  ${time}`;

  process.stdout.write(output);
  if (completed === total) {
    process.stdout.write('\n'); 
  }
}
function safeBind(...params) {
  return params.map(v => v === undefined ? null : v);
}

function normalizeKey(key) {
  if (!key) return null;
  return String(key).trim().toUpperCase();
}

const {
  DB_PG_SERVER, DB_PG_PORT, DB_PG_USERNAME, DB_PG_PASSWORD, DB_PG_NAME,
  DB_MYSQL_SERVER, DB_MYSQL_USERNAME, DB_MYSQL_PASSWORD, DB_MYSQL_NAME
} = envVariables;

const cachePacientes = new Map();
const cacheProfissionais = new Map();
const cacheProcedimentos = new Map();
const cacheUnidades = new Map();
const cacheSolicitacoesLaboratorio = new Map();

const chunkSize = 100;

async function precacheProcedimentos(mysqlConn) {
  const [rows] = await mysqlConn.execute(
    `SELECT id_procedimento, id_grupo_procedimento, codigo_sus, codigo_integracaoesus FROM reg_tb_procedimento`
  );
  for (const r of rows) {
    if (r.codigo_sus) cacheProcedimentos.set(r.codigo_sus.trim(), {
      id_procedimento: r.id_procedimento,
      id_grupo_procedimento: r.id_grupo_procedimento
    });
    if (r.codigo_integracaoesus) cacheProcedimentos.set(r.codigo_integracaoesus.trim(), {
      id_procedimento: r.id_procedimento,
      id_grupo_procedimento: r.id_grupo_procedimento
    });
  }
}

async function getIdProfissional(mysqlConn, row) {
  const chave = normalizeKey(row.cns_prof) || normalizeKey(row.cpf_prof);
  if (!chave) return null;
  if (cacheProfissionais.has(chave)) return cacheProfissionais.get(chave);

  const [res] = await mysqlConn.execute(
    `SELECT codigoprofissional_cnes FROM tb_profissional WHERE cns_profissional = ? OR cpf_profissional = ? LIMIT 1`,
    [row.cns_prof, row.cpf_prof]
  );

  if (res.length) {
    const id = res[0].codigoprofissional_cnes;
    cacheProfissionais.set(chave, id);
    return id;
  }
  return null;
}

async function getIdUnidade(mysqlConn, nu_cnes) {
  const chave = normalizeKey(nu_cnes);
  if (!chave) return null;
  if (cacheUnidades.has(chave)) return cacheUnidades.get(chave);

  const [res] = await mysqlConn.execute(
    `SELECT codigoestabelecimento_cnes FROM tb_estabelecimento WHERE cnes_estabelecimento = ? LIMIT 1`,
    [nu_cnes]
  );

  if (res.length) {
    const id = res[0].codigoestabelecimento_cnes;
    cacheUnidades.set(chave, id);
    return id;
  }
  return null;
}

async function getIdProcedimento(mysqlConn, codigo) {
  const originalCodigo = String(codigo).trim();
  return cacheProcedimentos.get(originalCodigo) || { id_procedimento: null, id_grupo_procedimento: null };
}

async function main() {
  const pgClient = new Client({
    host: DB_PG_SERVER,
    port: Number(DB_PG_PORT),
    user: DB_PG_USERNAME,
    password: DB_PG_PASSWORD,
    database: DB_PG_NAME,
    connectionTimeoutMillis: 20000
  });
    await pgClient.connect();

  const mysqlConn = await mysql.createConnection({
    host: DB_MYSQL_SERVER,
    user: DB_MYSQL_USERNAME,
    password: DB_MYSQL_PASSWORD,
    database: DB_MYSQL_NAME,
    multipleStatements: true
  });
    console.log(`precacheProcedimentos`);
  await precacheProcedimentos(mysqlConn);
  
   console.log(`Pré QUERY PG`);
  const query = `SELECT
    t1.co_seq_atend_prof as id_atend_esus,
    t7.nu_cpf as nu_cpf,
    t7.nu_cns as nu_cns,
    t7.no_cidadao,
    TO_CHAR(t7.dt_nascimento, 'YYYY-MM-DD') as dt_nascimento,
    t7.no_mae,
    CASE 
      WHEN t7.no_sexo ILIKE 'MASCULINO' THEN 'M'
      WHEN t7.no_sexo ILIKE 'FEMININO' THEN 'F'
      ELSE NULL
    END AS no_sexo,
    CASE
    	WHEN t19.co_ibge='' or t19.co_ibge is null then '291920'
    	WHEN t19.co_ibge is not null then left(t19.co_ibge,6)
    END AS co_ibge,
    t7.ds_cep,
    t7.ds_logradouro,
    t7.no_bairro,
    t7.nu_numero as numero_endereco,
    t7.nu_telefone_celular,
    TO_CHAR(t1.dt_inicio, 'YYYY-MM-DD') AS data_atendimento,
    TO_CHAR(t14.dt_requisicao, 'YYYY-MM-DD') AS data_requisicao,
    LEFT(t14.ds_justificativa_procedimento, 255) AS ds_justificativa_procedimento,
    t16.no_proced as procedimento,
    t16.co_proced as codigo_sigtap,
    COALESCE(t17.nu_cid10, 'R69') AS co_cid_justificativa_procedimento,
    t13.nu_cpf as cpf_prof,
    t13.nu_cns as cns_prof,
    t11.nu_cnes as nu_cnes,
    t20.nu_ine as ine_equipe,
    t20.no_equipe as nome_equipe
  FROM tb_atend_prof t1
  LEFT JOIN tb_atend t2 ON t2.co_atend_prof = t1.co_seq_atend_prof
  LEFT JOIN tb_prontuario t6 ON t2.co_prontuario = t6.co_seq_prontuario
  LEFT JOIN tb_cidadao t7 ON t6.co_cidadao = t7.co_seq_cidadao
  LEFT JOIN tb_lotacao t9 ON t1.co_lotacao = t9.co_ator_papel
  LEFT JOIN tb_unidade_saude t11 ON t9.co_unidade_saude = t11.co_seq_unidade_saude
  LEFT JOIN tb_prof t13 ON t9.co_prof = t13.co_seq_prof
  LEFT JOIN tb_requisicao_exame t14 ON t1.co_seq_atend_prof = t14.co_atend_prof
  LEFT JOIN tb_exame_requisitado t15 ON t14.co_seq_requisicao_exame = t15.co_requisicao_exame
  LEFT JOIN tb_proced t16 ON t15.co_proced = t16.co_seq_proced
  LEFT JOIN tb_cid10 t17 ON t14.co_cid10 = t17.co_cid10
  LEFT JOIN tb_proced_grupo t18 ON t16.co_proced_grupo = t18.co_proced_grupo
  LEFT JOIN tb_localidade t19 ON t7.co_localidade_endereco = t19.co_localidade
  LEFT JOIN tb_equipe t20 ON t20.co_seq_equipe = t9.co_equipe
  
  WHERE t7.no_cidadao IS NOT NULL
  AND t18.nu_ms = '09'
  ORDER BY t14.dt_requisicao DESC`;

  const { rows } = await pgClient.query(query);
   console.log(`Pós QUERY PG`);
  console.log(`Total de registros: ${rows.length}`);

  await mysqlConn.beginTransaction();
  try {
    for (let i = 0; i < rows.length; i++) {
      const r = rows[i];
      const cpf = r.nu_cpf?.replace(/\D/g, '') || null;
      const cns = r.nu_cns || null;
      const cpfKey = normalizeKey(cpf);
      const cnsKey = normalizeKey(cns);
      const key = cpfKey || cnsKey;
      if (!key) continue;

      let idPaciente = cachePacientes.get(key);
      if (!idPaciente) {
        const [pacienteRows] = await mysqlConn.execute(
          `SELECT id_paciente FROM tb_paciente WHERE cpf_paciente = ? OR cns_paciente = ? LIMIT 1`,
          [cpf, cns]
        );
        if (pacienteRows.length) {
          idPaciente = pacienteRows[0].id_paciente;
        } else {
          const [insertResult] = await mysqlConn.execute(
          `INSERT INTO tb_paciente (
            cpf_paciente, cns_paciente, nome_paciente, datanasc, sexo, nomemae,
            cep_paciente, endereco_paciente, numeroend_paciente, bairro_paciente,
            celular1, origem, codigocidade
          ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'esus', ?)`,
          safeBind(
            cpf,
            cns,
            r.no_cidadao,
            r.dt_nascimento,
            r.no_sexo,
            r.no_mae,
            r.ds_cep,
            r.ds_logradouro,
            r.numero_endereco,
            r.no_bairro,
            r.nu_telefone_celular,
            r.co_ibge
          )
        );
        idPaciente = insertResult.insertId;
        }
        cachePacientes.set(key, idPaciente);
      }

      const idProf = await getIdProfissional(mysqlConn, r);
      const idUnidade = await getIdUnidade(mysqlConn, r.nu_cnes);
      const { id_procedimento, id_grupo_procedimento } = await getIdProcedimento(mysqlConn, r.codigo_sigtap);

      if (!id_procedimento && !id_grupo_procedimento) {
        continue;
      }
      const isLaboratorio = String(r.codigo_sigtap || '').trim().startsWith('0202') || 
                            String(r.codigo_sigtap || '').trim().startsWith('ABEX');

      if (isLaboratorio) {
        const chaveLab = `${key}|${normalizeKey(r.cns_prof)}|${r.data_atendimento}`;
        if (!cacheSolicitacoesLaboratorio.has(chaveLab)) {
          const [existeGrupo] = await mysqlConn.execute(
            `SELECT id_solicitacao FROM reg_tb_solicitacoes 
             WHERE id_paciente = ? AND id_grupo_procedimento = ? AND id_procedimento IS NULL AND id_statussolicitacao = 2 AND datahora_cadastro >= CURDATE() LIMIT 1`,
            [idPaciente, id_grupo_procedimento]
          );
          let idSolicitacao;
          if (existeGrupo.length) {
            idSolicitacao = existeGrupo[0].id_solicitacao;
          } else {
            const [res] = await mysqlConn.execute(
              `INSERT INTO reg_tb_solicitacoes (id_paciente, cid_solicitacao, codigoprofissional_cnes, id_grupo_procedimento, id_statussolicitacao, id_usuario_cadastrou, id_unidade_cadastrou, datahora_cadastro)
              VALUES (?, ?, ?, ?, 2, 1, ?, NOW())`,
              safeBind(idPaciente, r.co_cid_justificativa_procedimento, idProf, id_grupo_procedimento, idUnidade)
            );

            idSolicitacao = res.insertId;
            const protocolo = `${moment().format('YYYYMM')}${idSolicitacao.toString().padStart(5, '0')}`;
            await mysqlConn.execute(
              `UPDATE reg_tb_solicitacoes SET numero_procotolo = ? WHERE id_solicitacao = ?`,
              [protocolo, idSolicitacao]
            );
          }
          cacheSolicitacoesLaboratorio.set(chaveLab, idSolicitacao);
          continue;
        }
      } else {
        const [existe] = await mysqlConn.execute(
          `SELECT 1 FROM reg_tb_solicitacoes WHERE id_paciente = ? AND id_procedimento = ? AND id_statussolicitacao = 2 LIMIT 1`,
          [idPaciente, id_procedimento]
        );
        if (existe.length) continue;

        const [res] = await mysqlConn.execute(
          `INSERT INTO reg_tb_solicitacoes (id_paciente, cid_solicitacao, codigoprofissional_cnes, id_grupo_procedimento, id_procedimento, observacoes_solicitacao, id_statussolicitacao, id_usuario_cadastrou, id_unidade_cadastrou, datahora_cadastro)
          VALUES (?, ?, ?, ?, ?, ?, 2, 1, ?, NOW())`,
          safeBind(idPaciente, r.co_cid_justificativa_procedimento, idProf, id_grupo_procedimento, id_procedimento, r.ds_justificativa_procedimento, idUnidade)
        );


        const protocolo = `${moment().format('YYYYMM')}${res.insertId.toString().padStart(5, '0')}`;
        await mysqlConn.execute(
          `UPDATE reg_tb_solicitacoes SET numero_procotolo = ? WHERE id_solicitacao = ?`,
          [protocolo, res.insertId]
        );
      }

      if ((i + 1) % chunkSize === 0 || i + 1 === rows.length) {
        printProgressBar(i + 1, rows.length);
      }
    }

    for (const [chaveLab, idSolicitacao] of cacheSolicitacoesLaboratorio.entries()) {
      const [keyPaciente, cnsProf, dataAtend] = chaveLab.split('|');

      const insercoes = [];
      for (let i = 0; i < rows.length; i++) {
        const p = rows[i];

        const matchPaciente = (normalizeKey(p.nu_cns) === keyPaciente || normalizeKey(p.nu_cpf) === keyPaciente);
        const matchProf = normalizeKey(p.cns_prof) === cnsProf;
        const matchData = p.data_atendimento === dataAtend;
        const isLab = p.codigo_sigtap?.startsWith('ABEX') || p.codigo_sigtap?.startsWith('0202');

        if (matchPaciente && matchProf && matchData && isLab) {
          insercoes.push((async () => {
            const { id_procedimento: idProcRelacionado } = await getIdProcedimento(mysqlConn, p.codigo_sigtap);
            if (!idProcRelacionado) return;
            const [existeLab] = await mysqlConn.execute(
              `SELECT 1 FROM reg_tb_solicitacoes_laboratorio WHERE id_solicitacao = ? AND id_procedimento_solicitado = ? LIMIT 1`,
              [idSolicitacao, idProcRelacionado]
            );
            if (!existeLab.length) {
              await mysqlConn.execute(
                `INSERT INTO reg_tb_solicitacoes_laboratorio (id_solicitacao, id_procedimento_solicitado) VALUES (?, ?)`,
                [idSolicitacao, idProcRelacionado]
              );
            }
          })());
        }
      }
      await Promise.allSettled(insercoes);
    }

    await mysqlConn.commit();
    console.log('\nImportação concluída com sucesso.');
  } catch (err) {
    await mysqlConn.rollback();
    console.error('\nErro durante processamento:', err);
  } finally {
    await pgClient.end();
    await mysqlConn.end();
  }
}

main();