<?php
/**
 * Biblioteca compartilhada de sincronização de integrações do tipo "origem".
 * Usada por: integracoes_actions.php, realizado_actions.php, run_integracoes_schedule.php
 *
 * Todas as funções recebem $pdo como parâmetro (sem global).
 */

/**
 * Busca integrações do tipo "origem" ativas que referenciam uma integração conexão/api
 * @return array Lista de ['cdintegracao' => int, 'dsintegracao' => string]
 */
function origemBuscarVinculadas(int $cdintegracao, PDO $pdo): array {
  $sql = "SELECT DISTINCT i.cdintegracao, i.dsintegracao
            FROM back.integracao_conexao ic
            JOIN back.integracao i ON i.cdintegracao = ic.cdintegracao
           WHERE ic.cdintegracao_conexao = :id
             AND i.flativo = true
             AND i.fltipo = 'origem'
           ORDER BY i.cdintegracao";
  $stmt = $pdo->prepare($sql);
  $stmt->execute([':id' => $cdintegracao]);
  return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

/**
 * Obtém os dados de conexão de TODAS as integrações referenciadas por uma integração "origem"
 * Exclui conexões API (sem tipo_banco)
 * @return array Array de configurações de conexão
 */
function origemObterConexoes(int $cdintegracao, PDO $pdo): array {
  $sql = "SELECT i.cdintegracao, i.dsintegracao, i.tipo_banco, i.host, i.porta, i.dbname, i.usuario, i.senha_encrypted, i.schema_banco
          FROM back.integracao_conexao ic
          JOIN back.integracao i ON i.cdintegracao = ic.cdintegracao_conexao
          WHERE ic.cdintegracao = :id AND i.flativo = true AND i.tipo_banco IS NOT NULL AND i.tipo_banco != ''";
  $stmt = $pdo->prepare($sql);
  $stmt->execute([':id' => $cdintegracao]);
  return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

/**
 * Core da sincronização de uma integração do tipo "origem".
 * Conecta-se aos bancos referenciados (múltiplos), executa o SQL, e armazena dados em back.origem_dados.
 * @return array ['ok' => bool, 'msg' => string, 'total' => int, 'ignorados' => int, 'erros' => int, 'mensagens' => array]
 */
function origemExecutarSincronizacao(int $id, PDO $pdo): array {
  try {
    // Buscar configuração da integração origem
    $stmt = $pdo->prepare('SELECT cdintegracao, fltipo, sql_origem, campo_empresa FROM back.integracao WHERE cdintegracao = :id AND flativo = true');
    $stmt->execute([':id' => $id]);
    $integracao = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$integracao) return ['ok' => false, 'msg' => 'Integração origem #' . $id . ' não encontrada ou inativa', 'total' => 0, 'ignorados' => 0, 'erros' => 0];
    if ($integracao['fltipo'] !== 'origem') return ['ok' => false, 'msg' => 'Integração #' . $id . ' não é do tipo "origem"', 'total' => 0, 'ignorados' => 0, 'erros' => 0];

    // Buscar TODAS as conexões referenciadas
    $conexoes = origemObterConexoes($id, $pdo);
    if (empty($conexoes)) return ['ok' => false, 'msg' => 'Origem #' . $id . ': Nenhuma conexão de referência ativa', 'total' => 0, 'ignorados' => 0, 'erros' => 0];

    $sqlOrigem = trim($integracao['sql_origem'] ?? '');
    if (!$sqlOrigem) return ['ok' => false, 'msg' => 'Origem #' . $id . ': SQL de origem vazio', 'total' => 0, 'ignorados' => 0, 'erros' => 0];

    // Revalidar segurança
    if (!preg_match('/^\s*(SELECT|WITH)\b/i', $sqlOrigem)) {
      return ['ok' => false, 'msg' => 'Origem #' . $id . ': SQL inválido', 'total' => 0, 'ignorados' => 0, 'erros' => 0];
    }

    $campoEmpresa = trim($integracao['campo_empresa'] ?? '');

    // Carregar mapa de empresas: back.empresa.cdintegracao (código ERP) → cdempresa (ID Helm)
    $mapaEmpresas = [];
    $stmtEmp = $pdo->query("SELECT cdempresa, cdintegracao FROM back.empresa WHERE cdintegracao IS NOT NULL AND cdintegracao != ''");
    while ($rowEmp = $stmtEmp->fetch(PDO::FETCH_ASSOC)) {
      $mapaEmpresas[trim($rowEmp['cdintegracao'])] = (int)$rowEmp['cdempresa'];
    }

    // Próximo lote
    $stmtLote = $pdo->prepare("SELECT COALESCE(MAX(nrlote), 0) + 1 FROM back.origem_dados WHERE cdintegracao = :id");
    $stmtLote->execute([':id' => $id]);
    $nrLote = (int)$stmtLote->fetchColumn();

    // Verificar se esta origem tem pai fltipo='api'
    // Se tiver, NÃO deletar dados anteriores (a API/PS1 gerencia esses dados)
    $stmtPaiApi = $pdo->prepare("
        SELECT COUNT(*) FROM back.integracao_conexao ic
        JOIN back.integracao i ON i.cdintegracao = ic.cdintegracao_conexao
        WHERE ic.cdintegracao = :id AND i.fltipo = 'api' AND i.flativo = true
    ");
    $stmtPaiApi->execute([':id' => $id]);
    $temPaiApi = ((int)$stmtPaiApi->fetchColumn() > 0);

    if (!$temPaiApi) {
        // Origem só tem pais do tipo conexão — limpar dados anteriores antes de reimportar
        $pdo->prepare('DELETE FROM back.origem_dados WHERE cdintegracao = :id')->execute([':id' => $id]);
    }

    $stmtInsert = $pdo->prepare('INSERT INTO back.origem_dados (cdintegracao, cdempresa, dados, valor, nrlote) VALUES (:cd, :cdempresa, :dados, :valor, :nrlote)');

    $totalInseridos = 0;
    $totalIgnorados = 0;
    $totalErros = 0;
    $mensagens = [];

    foreach ($conexoes as $cfg) {
      $nomeConex = $cfg['dsintegracao'] ?? 'Conexão #' . $cfg['cdintegracao'];
      try {
        // 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 {
          $mensagens[] = "✗ {$nomeConex}: Tipo de banco não suportado ({$tipo})";
          continue;
        }

        $pdoOrigem = new PDO($dsn, $usuario, $senha, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
        $stmtOrigem = $pdoOrigem->query($sqlOrigem);

        $inseridos = 0;
        $ignorados = 0;
        $erros = 0;

        // Detectar nomes reais das colunas (case-insensitive)
        $primeiraLinha = $stmtOrigem->fetch(PDO::FETCH_ASSOC);
        if (!$primeiraLinha) {
          $mensagens[] = "✓ {$nomeConex}: 0 registros (vazio)";
          continue;
        }

        $campoEmpresaReal = null;
        $colunaValor = null;
        $colunasSelect = array_keys($primeiraLinha);

        // Validar: o SELECT deve retornar exatamente 2 colunas (campo_empresa + 1 valor)
        if (count($colunasSelect) !== 2) {
          $mensagens[] = "✗ {$nomeConex}: O SELECT deve retornar exatamente 2 colunas (campo empresa + 1 coluna de valor), mas retornou " . count($colunasSelect) . " colunas: " . implode(', ', $colunasSelect);
          continue;
        }

        foreach ($colunasSelect as $col) {
          if ($campoEmpresa !== '' && strtoupper($col) === strtoupper($campoEmpresa)) {
            $campoEmpresaReal = $col;
          } else {
            $colunaValor = $col; // A outra coluna é automaticamente a coluna de valor
          }
        }

        if ($campoEmpresa !== '' && $campoEmpresaReal === null) {
          $mensagens[] = "✗ {$nomeConex}: Coluna '{$campoEmpresa}' não encontrada no SELECT. Colunas disponíveis: " . implode(', ', $colunasSelect);
          continue;
        }

        // Processar primeira linha + restante
        $allRows = array_merge([$primeiraLinha], $stmtOrigem->fetchAll(PDO::FETCH_ASSOC));

        foreach ($allRows as $reg) {
          try {
            // Resolver empresa: valor do campo_empresa → back.empresa.cdintegracao → cdempresa
            $cdempresa = null;
            if ($campoEmpresaReal !== null && isset($reg[$campoEmpresaReal])) {
              $valorEmpresaERP = trim((string)$reg[$campoEmpresaReal]);
              $cdempresa = $mapaEmpresas[$valorEmpresaERP] ?? null;
              if ($cdempresa === null) {
                $ignorados++;
                continue; // Empresa do ERP não mapeada no Helm
              }
            }

            // Extrair valor da coluna de dados (a que NÃO é campo_empresa)
            $valor = null;
            if ($colunaValor !== null && isset($reg[$colunaValor])) {
              $valor = (string)$reg[$colunaValor];
            }

            $stmtInsert->execute([
              ':cd' => $id,
              ':cdempresa' => $cdempresa,
              ':dados' => json_encode($reg, JSON_UNESCAPED_UNICODE),
              ':valor' => $valor,
              ':nrlote' => $nrLote
            ]);
            $inseridos++;
          } catch (Exception $e) {
            $erros++;
          }
        }

        $totalInseridos += $inseridos;
        $totalIgnorados += $ignorados;
        $totalErros += $erros;
        $msg = "✓ {$nomeConex}: {$inseridos} registros";
        if ($ignorados > 0) $msg .= " ({$ignorados} ignorados — empresa não mapeada)";
        if ($erros > 0) $msg .= " ({$erros} erros)";
        $mensagens[] = $msg;
      } catch (Exception $e) {
        $totalErros++;
        $mensagens[] = "✗ {$nomeConex}: " . $e->getMessage();
      }
    }

    // Atualizar data de última sincronização
    $pdo->prepare('UPDATE back.integracao SET ultima_sincronizacao = NOW() WHERE cdintegracao = :id')->execute([':id' => $id]);

    $msg = "Sincronização concluída: {$totalInseridos} registros importados de " . count($conexoes) . " conexão(ões)";
    if ($totalIgnorados > 0) $msg .= ", {$totalIgnorados} ignorados (empresa não mapeada no Helm)";
    if ($totalErros > 0) $msg .= ", {$totalErros} erros";
    $msg .= "\n\n" . implode("\n", $mensagens);

    return [
      'ok' => true,
      'msg' => $msg,
      'total' => $totalInseridos,
      'ignorados' => $totalIgnorados,
      'erros' => $totalErros,
      'mensagens' => $mensagens
    ];
  } catch (Exception $e) {
    return ['ok' => false, 'msg' => 'Erro na sincronização origem #' . $id . ': ' . $e->getMessage(), 'total' => 0, 'ignorados' => 0, 'erros' => 0];
  }
}
