<?php
require_once(dirname(__DIR__) . '/config/db.php');

ini_set('display_errors', '0');
error_reporting(E_ALL);

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

function json_out($arr, $code = 200) {
  http_response_code($code);
  header('Content-Type: application/json; charset=utf-8');
  echo json_encode($arr, JSON_UNESCAPED_UNICODE);
  exit;
}

set_error_handler(function($severity, $message, $file, $line) {
  if (!(error_reporting() & $severity)) return false;
  throw new ErrorException($message, 0, $severity, $file, $line);
});

set_exception_handler(function($e) {
  $loc = basename($e->getFile()) . ':' . $e->getLine();
  json_out(['ok' => false, 'msg' => 'Erro interno (' . $loc . '): ' . $e->getMessage()], 500);
});

/**
 * Valida SQL de origem - APENAS SELECT é aceito
 * Bloqueado: INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, EXEC, etc
 * Permitido: SELECT, WITH (CTEs)
 */
function validarSQLOrigem(string $sql): bool {
  $sqlTrim = trim($sql);
  if ($sqlTrim === '') return false;
  if (strpos($sqlTrim, ';') !== false) return false;
  if (!preg_match('/^\s*(SELECT|WITH)\b/i', $sqlTrim)) return false;
  $bloqueados = ['INSERT','UPDATE','DELETE','MERGE','TRUNCATE','DROP','ALTER','CREATE','GRANT','REVOKE','COMMENT','EXEC','EXECUTE','CALL'];
  foreach ($bloqueados as $cmd) {
    if (preg_match('/\b' . $cmd . '\b/i', $sqlTrim)) return false;
  }
  return true;
}

function extrairColunasSelect(string $sql): array {
  if (!preg_match('/\bSELECT\b(.*?)\bFROM\b/is', $sql, $m)) return [];
  $lista = $m[1];
  $colunas = [];
  $buffer = '';
  $nivel = 0;
  
  for ($i = 0, $len = strlen($lista); $i < $len; $i++) {
    $ch = $lista[$i];
    if ($ch === '(') {
      $nivel++;
      $buffer .= $ch;
    } elseif ($ch === ')') {
      $nivel--;
      $buffer .= $ch;
    } elseif ($ch === ',' && $nivel === 0) {
      $expr = trim($buffer);
      if ($expr !== '') $colunas[] = $expr;
      $buffer = '';
    } else {
      $buffer .= $ch;
    }
  }
  if (trim($buffer) !== '') $colunas[] = trim($buffer);

  $nomes = [];
  foreach ($colunas as $expr) {
    if (preg_match('/\s+AS\s+([^\s,]+)$/i', $expr, $mAs)) {
      $alias = $mAs[1];
    } else {
      $tmp = preg_replace('/[\)]+$/', '', $expr);
      $partes = preg_split('/\s+/', trim($tmp));
      $ultimo = end($partes);
      $subPartes = explode('.', $ultimo);
      $alias = end($subPartes);
    }
    $alias = trim($alias, "`\"'[] ");
    if ($alias === '' || $alias === '*') continue;
    $nomes[] = strtolower($alias);
  }
  
  return array_values(array_unique($nomes));
}

function validarCamposObrigatoriosSQLOrigem(string $sql): array {
  $obrigatorios = [
    'cdorigem' => 'Código de origem do lançamento (identificador no ERP)',
    'cdcentrocusto' => 'Código do centro de custo',
    'cdplanocontabil' => 'Código do plano contábil',
    'dtmovimento' => 'Data do movimento (determina mesref/anoref/cdexercicio)',
    'tpmovimento' => 'Tipo de movimento: D (Débito) ou C (Crédito)',
    'valor' => 'Valor do lançamento',
    'dscomplemento' => 'Descrição/complemento',
    'cdempresa' => 'Código da empresa na origem (relacionamento com back.empresa.cdintegracao)'
  ];
  $colunas = extrairColunasSelect($sql);
  $faltando = [];
  
  foreach ($obrigatorios as $campo => $desc) {
    if (!in_array(strtolower($campo), $colunas)) {
      $faltando[] = "$campo ($desc)";
    }
  }
  
  return $faltando;
}

function validarCamposObrigatorios(array $d, bool $criacao): array {
  $erros = [];
  $req = ['dsintegracao','tipo_banco','host','porta','dbname','usuario','sql_origem'];
  foreach ($req as $campo) if (empty($d[$campo])) $erros[] = "Campo obrigatório: $campo";
  if ($criacao && empty($d['senha'])) $erros[] = 'Senha obrigatória na criação';
  if (!empty($d['sql_origem'])) {
    if (!validarSQLOrigem($d['sql_origem'])) {
      $erros[] = 'SQL de origem inválido. Apenas instruções SELECT são aceitas (sem INSERT, UPDATE, DELETE, DROP, ALTER, etc)';
    } else {
      $faltando = validarCamposObrigatoriosSQLOrigem($d['sql_origem']);
      if (!empty($faltando)) {
        $erros[] = 'Campos obrigatórios ausentes no SELECT: ' . implode(', ', $faltando);
      }
    }
  }
  // Agendamento simplificado: apenas "mínimo de horas" (0 = manual)
  $minHoras = (int)($d['min_horas_atualizacao'] ?? 0);
  if ($minHoras < 0) $erros[] = 'Mínimo de horas inválido';
  return $erros;
}

function bindComuns(PDOStatement $stmt, array $d, bool $comSenha) {
  $minHoras = (int)($d['min_horas_atualizacao'] ?? 0);
  if ($minHoras < 0) $minHoras = 0;
  $autoAtivo = ($minHoras > 0);

  $stmt->bindValue(':dsintegracao', $d['dsintegracao']);
  $stmt->bindValue(':flativo', !empty($d['flativo']), PDO::PARAM_BOOL);
  $stmt->bindValue(':tipo_banco', $d['tipo_banco']);
  $stmt->bindValue(':host', $d['host']);
  $stmt->bindValue(':porta', (int)($d['porta'] ?? 5432), PDO::PARAM_INT);
  $stmt->bindValue(':schema_banco', $d['schema_banco'] ?? 'public');
  $stmt->bindValue(':dbname', $d['dbname']);
  $stmt->bindValue(':usuario', $d['usuario']);
  if ($comSenha) $stmt->bindValue(':senha_encrypted', $d['senha'] ?? '');
  $stmt->bindValue(':sql_origem', $d['sql_origem']);
  // Persistimos o número (horas) em schedule_cron e derivamos schedule_ativo
  $stmt->bindValue(':schedule_cron', $autoAtivo ? (string)$minHoras : null);
  $stmt->bindValue(':schedule_ativo', $autoAtivo, PDO::PARAM_BOOL);
}

function salvarEmpresas(int $id, array $empresas) {
  global $pdo;
  $pdo->prepare('DELETE FROM back.integracao_empresa WHERE cdintegracao = :i')->execute([':i' => $id]);
  if (empty($empresas)) return;
  $stmt = $pdo->prepare('INSERT INTO back.integracao_empresa (cdintegracao, cdempresa) VALUES (:i, :e)');
  foreach ($empresas as $emp) {
    if ($emp === '' || $emp === null) continue;
    $stmt->execute([':i' => $id, ':e' => (int)$emp]);
  }
}

function salvarHorarios(int $id, array $horarios) {
  global $pdo;
  $pdo->prepare('DELETE FROM back.integracao_horarios WHERE cdintegracao = :i')->execute([':i' => $id]);
  if (empty($horarios)) return;
  $stmt = $pdo->prepare('INSERT INTO back.integracao_horarios (cdintegracao, hora, minuto, flativo) VALUES (:i, :h, :m, :f)');
  foreach ($horarios as $horario) {
    if (!isset($horario['hora']) || !isset($horario['minuto'])) continue;
    $hora = (int)$horario['hora'];
    $minuto = (int)$horario['minuto'];
    if ($hora < 0 || $hora > 23 || $minuto < 0 || $minuto > 59) continue;
    $ativo = isset($horario['flativo']) ? (bool)$horario['flativo'] : true;
    $stmt->execute([':i' => $id, ':h' => $hora, ':m' => $minuto, ':f' => $ativo]);
  }
}

function conectarTeste(array $cfg): array {
  $tipo = $cfg['tipo_banco'] ?? '';
  $porta = (int)($cfg['porta'] ?? 0);
  $host = $cfg['host'] ?? '';
  $dbname = $cfg['dbname'] ?? '';
  $usuario = $cfg['usuario'] ?? '';
  $senha = $cfg['senha'] ?? '';
  $schema = $cfg['schema_banco'] ?? '';

  if (!$tipo || !$host || !$dbname || !$usuario) return ['ok' => false, 'msg' => 'Campos de conexão incompletos'];

  try {
    if ($tipo === 'pgsql') {
      $dsn = "pgsql:host={$host};port=" . ($porta ?: 5432) . ";dbname={$dbname}" . ($schema ? ";options='--search_path=" . $schema . "'" : '');
    } elseif ($tipo === 'mysql') {
      $dsn = "mysql:host={$host};port=" . ($porta ?: 3306) . ";dbname={$dbname}";
    } elseif ($tipo === 'sqlsrv') {
      $dsn = "sqlsrv:Server={$host}," . ($porta ?: 1433) . ";Database={$dbname}";
    } else {
      return ['ok' => false, 'msg' => 'Tipo de banco não suportado'];
    }

    $tmp = new PDO($dsn, $usuario, $senha, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_TIMEOUT => 5,
    ]);
    unset($tmp);
    return ['ok' => true, 'msg' => 'Conexão bem-sucedida'];
  } catch (Exception $e) {
    return ['ok' => false, 'msg' => 'Falha: ' . $e->getMessage()];
  }
}

/**
 * Refresh das Materialized Views para consolidação de dados
 * Executado após sincronização bem-sucedida
 */
function refreshMaterializedViews(): array {
  global $pdo;
  
  $views = [
    'back.v_realizado_conta_mes_por_dia',
    'back.v_realizado_conta_mes',
    'back.v_orcado_conta_mes'
  ];
  
  $resultado = [
    'ok' => true,
    'views_atualizadas' => 0,
    'mensagens' => []
  ];
  
  foreach ($views as $view) {
    try {
      try {
        $sql = "REFRESH MATERIALIZED VIEW CONCURRENTLY {$view}";
        $pdo->exec($sql);
        $resultado['views_atualizadas']++;
        $resultado['mensagens'][] = "✓ {$view} consolidada";
      } catch (Exception $eConcurrent) {
        // Fallback: sem CONCURRENTLY (evita falhas por índice único ausente
        // ou erro de transaction block; pode bloquear leitores durante o refresh)
        $sql = "REFRESH MATERIALIZED VIEW {$view}";
        $pdo->exec($sql);
        $resultado['views_atualizadas']++;
        $resultado['mensagens'][] = "⚠ {$view} consolidada (fallback sem CONCURRENTLY)";
      }
    } catch (Exception $e) {
      $resultado['ok'] = false;
      $resultado['mensagens'][] = "✗ Erro ao consolidar {$view}: " . $e->getMessage();
    }
  }
  
  return $resultado;
}

$ajax = $_GET['ajax'] ?? '';
if ($ajax === '') { http_response_code(204); exit; }

switch ($ajax) {
  case 'criar': criarIntegracao(); break;
  case 'editar': editarIntegracao(); break;
  case 'obter': obterIntegracao(); break;
  case 'deletar': deletarIntegracao(); break;
  case 'testar': testarConexaoExistente(); break;
  case 'testar_temp': testarConexaoTemp(); break;
  case 'obter_integracoes': obterIntegracoes(); break;
  case 'sincronizar_agora': sincronizarAgora(); break;
  case 'verificar_dados': verificarDados(); break;
  default: json_out(['ok' => false, 'msg' => 'Ação inválida'], 400);
}

function criarIntegracao() {
  global $pdo;
  $data = json_decode(file_get_contents('php://input'), true) ?? [];
  $erros = validarCamposObrigatorios($data, true);
  if ($erros) json_out(['ok' => false, 'msg' => implode(' | ', $erros)], 400);

  try {
    $sql = "INSERT INTO back.integracao (dsintegracao, flativo, tipo_banco, host, porta, schema_banco, dbname, usuario, senha_encrypted, sql_origem, schedule_cron, schedule_ativo)
            VALUES (:dsintegracao, :flativo, :tipo_banco, :host, :porta, :schema_banco, :dbname, :usuario, :senha_encrypted, :sql_origem, :schedule_cron, :schedule_ativo)
            RETURNING cdintegracao";
    $stmt = $pdo->prepare($sql);
    bindComuns($stmt, $data, true);
    $stmt->execute();
    $id = (int)$stmt->fetchColumn();

    salvarEmpresas($id, $data['empresas'] ?? []);
    salvarHorarios($id, $data['horarios'] ?? []);
    json_out(['ok' => true, 'msg' => 'Integração criada', 'cdintegracao' => $id]);
  } catch (Exception $e) {
    json_out(['ok' => false, 'msg' => 'Erro ao criar: ' . $e->getMessage()], 500);
  }
}

function editarIntegracao() {
  global $pdo;
  $id = (int)($_GET['cdintegracao'] ?? 0);
  if ($id <= 0) json_out(['ok' => false, 'msg' => 'ID inválido'], 400);
  $data = json_decode(file_get_contents('php://input'), true) ?? [];
  $erros = validarCamposObrigatorios($data, false);
  if ($erros) json_out(['ok' => false, 'msg' => implode(' | ', $erros)], 400);

  try {
    $campos = "dsintegracao = :dsintegracao, flativo = :flativo, tipo_banco = :tipo_banco, host = :host, porta = :porta, schema_banco = :schema_banco, dbname = :dbname, usuario = :usuario, sql_origem = :sql_origem, schedule_cron = :schedule_cron, schedule_ativo = :schedule_ativo";
    $incluiSenha = !empty($data['senha']);
    if ($incluiSenha) $campos .= ", senha_encrypted = :senha_encrypted";

    $sql = "UPDATE back.integracao SET $campos WHERE cdintegracao = :id";
    $stmt = $pdo->prepare($sql);
    bindComuns($stmt, $data, $incluiSenha);
    $stmt->bindValue(':id', $id, PDO::PARAM_INT);
    $stmt->execute();

    salvarEmpresas($id, $data['empresas'] ?? []);
    salvarHorarios($id, $data['horarios'] ?? []);
    json_out(['ok' => true, 'msg' => 'Integração atualizada']);
  } catch (Exception $e) {
    json_out(['ok' => false, 'msg' => 'Erro ao atualizar: ' . $e->getMessage()], 500);
  }
}

function obterIntegracao() {
  global $pdo;
  $id = (int)($_GET['cdintegracao'] ?? 0);
  if ($id <= 0) json_out(['ok' => false, 'msg' => 'ID inválido'], 400);

  try {
    $stmt = $pdo->prepare("SELECT cdintegracao, dsintegracao, flativo, tipo_banco, host, porta, schema_banco, dbname, usuario, sql_origem, schedule_cron, schedule_ativo, ultima_sincronizacao FROM back.integracao WHERE cdintegracao = :id");
    $stmt->execute([':id' => $id]);
    $data = $stmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$data) {
      error_log("Integração não encontrada: ID=$id");
      json_out(['ok' => false, 'msg' => 'Integração não encontrada'], 404);
    }

    $stmtEmp = $pdo->prepare('SELECT cdempresa FROM back.integracao_empresa WHERE cdintegracao = :i');
    $stmtEmp->execute([':i' => $id]);
    $data['empresas'] = $stmtEmp->fetchAll(PDO::FETCH_COLUMN);
    
    // Carregar horários agendados
    $stmtHor = $pdo->prepare('SELECT cdhorario, hora, minuto, flativo FROM back.integracao_horarios WHERE cdintegracao = :i ORDER BY hora, minuto');
    $stmtHor->execute([':i' => $id]);
    $data['horarios'] = $stmtHor->fetchAll(PDO::FETCH_ASSOC);
    
    $data['schedule_ativo'] = ($data['schedule_ativo'] === true || $data['schedule_ativo'] === 't' || $data['schedule_ativo'] === 1 || $data['schedule_ativo'] === '1');

    // Exposição do campo simplificado para o front
    $minHoras = 0;
    if ($data['schedule_ativo']) {
      $minHoras = (int)($data['schedule_cron'] ?? 0);
      if ($minHoras <= 0) $minHoras = 24;
    }
    $data['min_horas_atualizacao'] = $minHoras;

    error_log("Integração carregada: ID=$id, Data=" . json_encode($data));
    json_out(['ok' => true, 'data' => $data]);
  } catch (Exception $e) {
    error_log("Erro ao obter integração ID=$id: " . $e->getMessage());
    json_out(['ok' => false, 'msg' => 'Erro: ' . $e->getMessage()], 500);
  }
}

function obterIntegracoes() {
  global $pdo;
  $res = $pdo->query("SELECT cdintegracao, dsintegracao FROM back.integracao WHERE flativo = true ORDER BY dsintegracao")->fetchAll(PDO::FETCH_ASSOC);
  json_out(['ok' => true, 'data' => $res]);
}

function deletarIntegracao() {
  global $pdo;
  $id = (int)($_GET['cdintegracao'] ?? 0);
  if ($id <= 0) json_out(['ok' => false, 'msg' => 'ID inválido'], 400);
  try {
    $pdo->beginTransaction();
    $pdo->prepare('DELETE FROM back.integracao_horarios WHERE cdintegracao = :i')->execute([':i' => $id]);
    $pdo->prepare('DELETE FROM back.integracao_empresa WHERE cdintegracao = :i')->execute([':i' => $id]);
    $pdo->prepare('DELETE FROM back.integracao WHERE cdintegracao = :i')->execute([':i' => $id]);
    $pdo->commit();
    json_out(['ok' => true, 'msg' => 'Integração removida']);
  } catch (Exception $e) {
    if ($pdo->inTransaction()) $pdo->rollBack();
    json_out(['ok' => false, 'msg' => 'Erro ao remover: ' . $e->getMessage()], 500);
  }
}

function testarConexaoExistente() {
  global $pdo;
  $id = (int)($_GET['cdintegracao'] ?? 0);
  if ($id <= 0) json_out(['ok' => false, 'msg' => 'ID inválido'], 400);

  $stmt = $pdo->prepare('SELECT tipo_banco, host, porta, dbname, usuario, senha_encrypted, schema_banco FROM back.integracao WHERE cdintegracao = :id');
  $stmt->execute([':id' => $id]);
  $cfg = $stmt->fetch(PDO::FETCH_ASSOC);
  if (!$cfg) json_out(['ok' => false, 'msg' => 'Integração não encontrada'], 404);
  $cfg['senha'] = $cfg['senha_encrypted'] ?? '';
  $res = conectarTeste($cfg);
  json_out($res, $res['ok'] ? 200 : 400);
}

function testarConexaoTemp() {
  $data = json_decode(file_get_contents('php://input'), true) ?? [];
  $res = conectarTeste($data);
  json_out($res, $res['ok'] ? 200 : 400);
}

function sincronizarAgora() {
  global $pdo;
  $id = (int)($_GET['cdintegracao'] ?? 0);
  if ($id <= 0) json_out(['ok' => false, 'msg' => 'ID inválido'], 400);

  try {
    // Buscar configuração da integração
    $stmt = $pdo->prepare('SELECT tipo_banco, host, porta, dbname, usuario, senha_encrypted, schema_banco, sql_origem FROM back.integracao WHERE cdintegracao = :id AND flativo = true');
    $stmt->execute([':id' => $id]);
    $cfg = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$cfg) json_out(['ok' => false, 'msg' => 'Integração não encontrada ou inativa'], 404);
    
    // Buscar empresas vinculadas (serão validadas contra cdempresa da origem)
    $stmtEmp = $pdo->prepare('SELECT cdempresa FROM back.integracao_empresa WHERE cdintegracao = :id');
    $stmtEmp->execute([':id' => $id]);
    $empresasPermitidas = $stmtEmp->fetchAll(PDO::FETCH_COLUMN);
    if (empty($empresasPermitidas)) json_out(['ok' => false, 'msg' => 'Nenhuma empresa vinculada'], 400);

    // Conectar no banco de origem
    $tipo = $cfg['tipo_banco'];
    $porta = (int)$cfg['porta'];
    $host = $cfg['host'];
    $dbname = $cfg['dbname'];
    $usuario = $cfg['usuario'];
    $senha = $cfg['senha_encrypted'] ?? '';
    $schema = $cfg['schema_banco'] ?? '';
    
    if ($tipo === 'pgsql') {
      $dsn = "pgsql:host={$host};port=" . ($porta ?: 5432) . ";dbname={$dbname}" . ($schema ? ";options='--search_path={$schema}'" : '');
    } elseif ($tipo === 'mysql') {
      $dsn = "mysql:host={$host};port=" . ($porta ?: 3306) . ";dbname={$dbname}";
    } elseif ($tipo === 'sqlsrv') {
      $dsn = "sqlsrv:Server={$host}," . ($porta ?: 1433) . ";Database={$dbname}";
    } else {
      json_out(['ok' => false, 'msg' => 'Tipo de banco não suportado'], 400);
    }

    $pdoOrigem = new PDO($dsn, $usuario, $senha, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
    
    // Construir SQL final - APENAS SELECT permitido (validado em validarSQLOrigem)
    $sqlOrigem = $cfg['sql_origem'];
    
    // Revalidar segurança antes de executar (defesa em profundidade - nada pode ser alterado na origem)
    if (!preg_match('/^\s*(SELECT|WITH)\b/i', trim($sqlOrigem))) {
      json_out(['ok' => false, 'msg' => 'Erro: SQL inválido'], 400);
    }
    
    $stmtOrigem = $pdoOrigem->query($sqlOrigem);
    $registros = $stmtOrigem->fetchAll(PDO::FETCH_ASSOC);
    
    if (empty($registros)) json_out(['ok' => true, 'msg' => 'Nenhum registro encontrado na origem', 'total' => 0]);
    
    $inseridos = 0;
    $erros = [];
    $ignorados = 0;
    
    // Gerar hash único para idlote (para controle interno desta sincronização)
    $idlote = md5($id . '_' . time() . '_' . count($registros));
    
    // Preparar insert no destino
    $sqlInsert = "INSERT INTO back.realizado (cdempresa, cdexercicio, cdcentrocusto, cdplanocontabil, mesref, anoref, tpmovimento, valor, dscomplemento, dtmovimento, cdintegracao, florigem, json_origem, flcancelado, dtcriacao, cdpessoa, idlote)
                  VALUES (:cdempresa, :cdexercicio, :cdcentrocusto, :cdplanocontabil, :mesref, :anoref, :tpmovimento, :valor, :dscomplemento, :dtmovimento, :cdintegracao, :florigem, :json_origem, :flcancelado, NOW(), :cdpessoa, :idlote)";
    $stmtInsert = $pdo->prepare($sqlInsert);
    
    foreach ($registros as $reg) {
      try {
        // Validar cdempresa da origem
        $cdempresa = $reg['cdempresa'] ?? null;
        if (!$cdempresa) {
          $erros[] = "Registro sem cdempresa: " . json_encode($reg);
          continue;
        }
        
        // Validar se empresa está vinculada à integração
        if (!in_array($cdempresa, $empresasPermitidas)) {
          $ignorados++;
          continue;
        }
        
        // Extrair ano e mês da data de movimento
        $dtmovimento = $reg['dtmovimento'] ?? null;
        if (!$dtmovimento) {
          $erros[] = "Registro sem dtmovimento para empresa {$cdempresa}";
          continue;
        }
        
        $dt = new DateTime($dtmovimento);
        $ano = (int)$dt->format('Y');
        $mes = (int)$dt->format('m');
        
        // Buscar exercício ativo para essa empresa e ano (baseado em dtmovimento)
        $stmtExerc = $pdo->prepare('SELECT cdexercicio FROM back.exercicio WHERE cdempresa = :emp AND anoref = :ano AND flativo = 0 LIMIT 1');
        $stmtExerc->execute([':emp' => $cdempresa, ':ano' => $ano]);
        $cdexercicio = $stmtExerc->fetchColumn();
        
        if (!$cdexercicio) {
          $erros[] = "Exercício não encontrado para empresa {$cdempresa}, ano {$ano} (origem: dtmovimento={$dtmovimento})";
          continue;
        }
          
        // Validar tipo de movimento
        $tpmovimento = strtoupper(trim($reg['tpmovimento'] ?? ''));
        if (!in_array($tpmovimento, ['D', 'C'])) {
          $erros[] = "Tipo de movimento inválido para empresa {$cdempresa}: '{$tpmovimento}' (deve ser D ou C)";
          continue;
        }
        
        // Inserir registro
        $stmtInsert->execute([
          ':cdempresa' => $cdempresa,
          ':cdexercicio' => $cdexercicio,
          ':cdcentrocusto' => $reg['cdcentrocusto'] ?? null,
          ':cdplanocontabil' => $reg['cdplanocontabil'] ?? null,
          ':mesref' => $mes,
          ':anoref' => $ano,
          ':tpmovimento' => $tpmovimento,
          ':valor' => $reg['valor'] ?? 0,
          ':dscomplemento' => $reg['dscomplemento'] ?? '',
          ':dtmovimento' => $dtmovimento,
          ':cdintegracao' => $id,
          ':florigem' => 2,
          ':json_origem' => json_encode($reg, JSON_UNESCAPED_UNICODE),
          ':flcancelado' => 0,
          ':cdpessoa' => 0,
          ':idlote' => $idlote
        ]);
        
        $inseridos++;
      } catch (Exception $e) {
        $erros[] = "Erro ao inserir registro empresa {$cdempresa}: " . $e->getMessage();
      }
    }
    
    // ✨ ETAPA 2: Consolidação dos dados (Refresh das Materialized Views)
    // Só considerar a execução finalizada quando o refresh retornar OK.
    $consolidacao = refreshMaterializedViews();

    // Atualizar data de última sincronização apenas quando consolidou com sucesso
    if (!empty($consolidacao['ok'])) {
      $pdo->prepare('UPDATE back.integracao SET ultima_sincronizacao = NOW() WHERE cdintegracao = :id')->execute([':id' => $id]);
    }
    
    $msg = "Sincronização concluída: {$inseridos} registros inseridos";
    if ($ignorados > 0) $msg .= ", {$ignorados} ignorados (empresa não vinculada)";
    if (!empty($erros)) {
      $msg .= ". Erros: " . implode('; ', array_slice($erros, 0, 5));
      if (count($erros) > 5) $msg .= " (+" . (count($erros) - 5) . " erros)";
    }
    
    // Adicionar status da consolidação
    $msg .= "\n\n📊 Consolidação de Dados:\n";
    $msg .= implode("\n", $consolidacao['mensagens']);
    
    json_out([
      'ok' => (bool)$consolidacao['ok'], 
      'msg' => $msg, 
      'total' => $inseridos, 
      'ignorados' => $ignorados, 
      'erros' => count($erros),
      'consolidacao' => $consolidacao
    ]);
    
  } catch (Exception $e) {
    json_out(['ok' => false, 'msg' => 'Erro na sincronização: ' . $e->getMessage()], 500);
  }
}

function verificarDados() {
  global $pdo;
  $id = (int)($_GET['cdintegracao'] ?? 0);
  if ($id <= 0) json_out(['ok' => false, 'msg' => 'ID inválido'], 400);

  try {
    // Buscar configuração da integração
    $stmt = $pdo->prepare('SELECT tipo_banco, host, porta, dbname, usuario, senha_encrypted, schema_banco, sql_origem FROM back.integracao WHERE cdintegracao = :id');
    $stmt->execute([':id' => $id]);
    $cfg = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$cfg) json_out(['ok' => false, 'msg' => 'Integração não encontrada'], 404);

    // Conectar no banco de origem
    $tipo = $cfg['tipo_banco'];
    $porta = (int)$cfg['porta'];
    $host = $cfg['host'];
    $dbname = $cfg['dbname'];
    $usuario = $cfg['usuario'];
    $senha = $cfg['senha_encrypted'] ?? '';
    $schema = $cfg['schema_banco'] ?? '';
    
    if ($tipo === 'pgsql') {
      $dsn = "pgsql:host={$host};port=" . ($porta ?: 5432) . ";dbname={$dbname}" . ($schema ? ";options='--search_path={$schema}'" : '');
    } elseif ($tipo === 'mysql') {
      $dsn = "mysql:host={$host};port=" . ($porta ?: 3306) . ";dbname={$dbname}";
    } elseif ($tipo === 'sqlsrv') {
      $dsn = "sqlsrv:Server={$host}," . ($porta ?: 1433) . ";Database={$dbname}";
    } else {
      json_out(['ok' => false, 'msg' => 'Tipo de banco desconhecido'], 400);
    }
    
    try {
      $pdoOrigin = new PDO($dsn, $usuario, $senha);
      $pdoOrigin->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
    } catch (Exception $e) {
      json_out(['ok' => false, 'msg' => 'Erro ao conectar na origem: ' . $e->getMessage()], 500);
    }
    
    // Buscar última linha do SQL de origem
    $data = json_decode(file_get_contents('php://input'), true) ?? [];
    $sql = trim($data['sql_origem'] ?? '');
    
    if (!$sql || !validarSQLOrigem($sql)) {
      json_out(['ok' => false, 'msg' => 'SQL de origem inválido'], 400);
    }
    
    // Executar SQL com LIMIT 1 ORDER BY descending (última linha) - APENAS SELECT permitido
    // Revalidar segurança antes de executar (defesa em profundidade - nada pode ser alterado na origem)
    if (!preg_match('/^\s*(SELECT|WITH)\b/i', trim($sql))) {
      json_out(['ok' => false, 'msg' => 'Erro: SQL inválido'], 400);
    }
    
    $sqlFinal = "$sql ORDER BY 1 DESC LIMIT 1";
    
    $stmtData = $pdoOrigin->prepare($sqlFinal);
    $stmtData->execute();
    $registro = $stmtData->fetch(PDO::FETCH_ASSOC);
    
    if ($registro === false) {
      json_out(['ok' => true, 'msg' => 'Nenhum registro encontrado', 'data' => []]);
    } else {
      json_out(['ok' => true, 'msg' => 'Último registro carregado', 'data' => $registro]);
    }
    
  } catch (Exception $e) {
    json_out(['ok' => false, 'msg' => 'Erro ao buscar dados: ' . $e->getMessage()], 500);
  }
}
?>
