<?php
/**
 * Sincronizador ERP → HELM (back.realizado - módulo específico)
 * 
 * Nova lógica simplificada:
 * - Uma integração = Uma conexão com SQL SELECT
 * - Não há mais modelo/origem/conexão em 3 níveis
 * - Requer período explícito (ano/mês) para sincronizar
 * - Deleta apenas o período especificado, não exercícios "abertos"
 */
class SincronizadorRealizado {
    private $pdoHelm;
    private $cdIntegracao;    // ID da integração (back.integracao)
    private $config;          // Configuração (tipo_banco, host, porta, dbname, usuario, senha_encrypted, sql_origem)
    private $anoRef;          // Ano a sincronizar
    private $mesRef;          // Mês a sincronizar
    private $cdEmpresa;       // Empresa da sessão

    public function __construct($pdoHelm, $cdIntegracao, $anoRef = null, $mesRef = null) {
        $this->pdoHelm = $pdoHelm;
        $this->cdIntegracao = (int)$cdIntegracao;
        $this->anoRef = $anoRef !== null ? (int)$anoRef : null;
        $this->mesRef = $mesRef !== null ? (int)$mesRef : null;
        $this->cdEmpresa = isset($_SESSION['cdempresa']) ? (int)$_SESSION['cdempresa'] : null;
        
        if (!$this->cdEmpresa) {
            throw new Exception('Empresa da sessão não encontrada');
        }
        
        $this->config = $this->carregarConfiguracao();
    }

    /**
     * Executar sincronização: DELETE período + INSERT dados da origem
     */
    public function sincronizar() {
        $inicio = microtime(true);

        try {
            // 1. Validar período obrigatório
            if ($this->anoRef === null || $this->mesRef === null) {
                throw new Exception('Período (ano/mês) é obrigatório para sincronizar realizado');
            }

            // 2. Validar SQL (apenas SELECT/WITH)
            if (empty($this->config['sql_origem'])) {
                throw new Exception('SQL da origem não configurada');
            }
            if (!$this->validarSQLLeituraApenas($this->config['sql_origem'])) {
                throw new Exception('SQL deve ser apenas SELECT ou WITH');
            }

            // 3. Conectar à origem
            $pdoOrigem = $this->conectarOrigem();

            // 4. Buscar dados da origem
            $stmtOrigem = $pdoOrigem->query($this->config['sql_origem']);
            $registrosOrigem = $stmtOrigem->fetchAll(PDO::FETCH_ASSOC);

            // 5. Iniciar transação
            $this->pdoHelm->beginTransaction();

            // 6. Deletar período
            $qtdDeletados = $this->deletarPeriodo($this->anoRef, $this->mesRef);

            // 7. Mapear + Validar + Inserir
            $resultado = $this->processarRegistros($registrosOrigem);

            // 8. Commit
            $this->pdoHelm->commit();

            $tempo = round(microtime(true) - $inicio, 2);

            return [
                'ok' => true,
                'qtd_origem' => count($registrosOrigem),
                'qtd_deletados' => $qtdDeletados,
                'qtd_inseridos' => $resultado['inseridos'],
                'qtd_ignorados' => $resultado['ignorados'],
                'qtd_erros' => $resultado['erros'],
                'tempo' => $tempo
            ];

        } catch (Exception $e) {
            if ($this->pdoHelm->inTransaction()) {
                $this->pdoHelm->rollBack();
            }
            throw $e;
        }
    }

    /**
     * Carregar configuração da integração
     */
    private function carregarConfiguracao() {
        $stmt = $this->pdoHelm->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' => $this->cdIntegracao]);
        $config = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$config) {
            throw new Exception('Integração não encontrada ou inativa');
        }

        return $config;
    }

    /**
     * Conectar à origem (ERP)
     */
    private function conectarOrigem() {
        try {
            $senha = base64_decode($this->config['senha_encrypted']);
            
            switch ($this->config['tipo_banco']) {
                case 'pgsql':
                    $dsn = sprintf(
                        "pgsql:host=%s;port=%d;dbname=%s",
                        $this->config['host'],
                        (int)$this->config['porta'],
                        $this->config['dbname']
                    );
                    break;
                case 'mysql':
                    $dsn = sprintf(
                        "mysql:host=%s;port=%d;dbname=%s",
                        $this->config['host'],
                        (int)$this->config['porta'],
                        $this->config['dbname']
                    );
                    break;
                case 'sqlsrv':
                    $dsn = sprintf(
                        "sqlsrv:Server=%s,%d;Database=%s",
                        $this->config['host'],
                        (int)$this->config['porta'],
                        $this->config['dbname']
                    );
                    break;
                default:
                    throw new Exception('Tipo de banco não suportado: ' . $this->config['tipo_banco']);
            }

            $pdo = new PDO($dsn, $this->config['usuario'], $senha, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]);

            // Define schema para PostgreSQL
            if ($this->config['tipo_banco'] === 'pgsql' && !empty($this->config['schema_banco'])) {
                $pdo->exec("SET search_path TO {$this->config['schema_banco']}, public");
            }

            return $pdo;

        } catch (PDOException $e) {
            throw new Exception('Erro ao conectar origem: ' . $e->getMessage());
        }
    }

    /**
     * Validar SQL - apenas SELECT/WITH permitido
     */
    private function validarSQLLeituraApenas(string $sql): bool {
        $sqlTrim = trim($sql);
        if ($sqlTrim === '') return false;

        // Bloqueia múltiplas instruções
        if (strpos($sqlTrim, ';') !== false) return false;

        // Deve começar com SELECT ou WITH
        if (!preg_match('/^\s*(SELECT|WITH)\b/i', $sqlTrim)) {
            return false;
        }

        // Bloqueia comandos perigosos
        $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;
    }

    /**
     * Deletar registros de um período (ano/mês) para a empresa
     */
    private function deletarPeriodo($ano, $mes) {
        $stmt = $this->pdoHelm->prepare("
            DELETE FROM back.realizado
            WHERE florigem = 2
              AND cdempresa = :emp
              AND anoref = :ano
              AND mesref = :mes
        ");
        $stmt->execute([
            ':emp' => $this->cdEmpresa,
            ':ano' => (int)$ano,
            ':mes' => (int)$mes
        ]);

        return $stmt->rowCount();
    }

    /**
     * Processar registros: mapear + validar + inserir
     */
    private function processarRegistros($registrosOrigem) {
        $inseridos = 0;
        $ignorados = 0;
        $erros = 0;
        $idlote = md5($this->cdIntegracao . '_' . time());

        $stmt = $this->pdoHelm->prepare("
            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
            )
        ");

        foreach ($registrosOrigem as $origem) {
            try {
                // Validar campos obrigatórios
                $cdempresa = $origem['cdempresa'] ?? null;
                $dtmovimento = $origem['dtmovimento'] ?? null;
                $tpmovimento = strtoupper(trim($origem['tpmovimento'] ?? ''));
                $valor = floatval($origem['valor'] ?? 0);
                $cdcentrocusto = $origem['cdcentrocusto'] ?? null;
                $cdplanocontabil = $origem['cdplanocontabil'] ?? null;

                // Validar obrigatoriedade
                if (!$cdempresa || !$dtmovimento || !$tpmovimento || !in_array($tpmovimento, ['D', 'C'])) {
                    $ignorados++;
                    continue;
                }

                // Parse data
                try {
                    $dt = new DateTime($dtmovimento);
                    $ano = (int)$dt->format('Y');
                    $mes = (int)$dt->format('m');
                } catch (Exception $e) {
                    $erros++;
                    continue;
                }

                // Buscar exercício
                $stmtExerc = $this->pdoHelm->prepare("
                    SELECT cdexercicio FROM back.exercicio
                    WHERE cdempresa = :emp AND anoref = :ano AND flativo = false LIMIT 1
                ");
                $stmtExerc->execute([':emp' => $cdempresa, ':ano' => $ano]);
                $cdexercicio = $stmtExerc->fetchColumn();

                if (!$cdexercicio) {
                    $erros++;
                    continue;
                }

                // Inserir
                $stmt->execute([
                    ':cdempresa' => $cdempresa,
                    ':cdexercicio' => $cdexercicio,
                    ':cdcentrocusto' => $cdcentrocusto,
                    ':cdplanocontabil' => $cdplanocontabil,
                    ':mesref' => $mes,
                    ':anoref' => $ano,
                    ':tpmovimento' => $tpmovimento,
                    ':valor' => $valor,
                    ':dscomplemento' => substr($origem['dscomplemento'] ?? '', 0, 500),
                    ':dtmovimento' => $dtmovimento,
                    ':cdintegracao' => $this->cdIntegracao,
                    ':florigem' => 2,
                    ':json_origem' => json_encode($origem, JSON_UNESCAPED_UNICODE),
                    ':flcancelado' => 0,
                    ':cdpessoa' => 0,
                    ':idlote' => $idlote
                ]);

                $inseridos++;

            } catch (Exception $e) {
                $erros++;
            }
        }

        return [
            'inseridos' => $inseridos,
            'ignorados' => $ignorados,
            'erros' => $erros
        ];
    }
}
