<?php
/**
 * WORKER DE PROCESSAMENTO - FILA DE SINCRONIZAÇÃO
 * 
 * Executa em background e processa registros da fila
 * Pode ser executado via cron: php erp_worker.php
 * 
 * Processamento:
 * - Prioridade: registros com menor valor primeiro
 * - Retry automático: até 5 tentativas por registro
 * - Transações: garante integridade dos dados
 */

require_once(dirname(__DIR__) . '/config/db.php');
require_once(dirname(__DIR__) . '/classes/MapeadorDados.php');

class FilaProcessador {
    private $pdo;
    private $lote_tamanho = 100; // Processar 100 registros por vez
    private $timeout = 300; // 5 minutos max por worker
    
    public function __construct($pdo) {
        $this->pdo = $pdo;
        set_time_limit($this->timeout);
    }
    
    /**
     * PROCESSAR FILA
     * Retorna número de registros processados
     */
    public function processar() {
        $inicio = time();
        $processados = 0;
        
        while ((time() - $inicio) < $this->timeout) {
            // Buscar próximos registros
            $registros = $this->buscarProximos();
            
            if (empty($registros)) {
                // Fila vazia, aguardar um pouco
                sleep(5);
                continue;
            }
            
            foreach ($registros as $item) {
                try {
                    $this->processarItem($item);
                    $processados++;
                } catch (Exception $e) {
                    $this->registrarErro($item['cdfila'], $e->getMessage());
                }
            }
        }
        
        echo "Worker finalizado. Registros processados: $processados\n";
        return $processados;
    }
    
    /**
     * BUSCAR PRÓXIMOS REGISTROS DA FILA
     */
    private function buscarProximos() {
        $stmt = $this->pdo->prepare("
            SELECT cdfila, cdintegracao, tipo_operacao, dados_json, qttenativas
            FROM back.integracao_fila
            WHERE flstatus = 'pendente'
            ORDER BY flprioridade ASC, dthcriacao ASC
            LIMIT :limit
            FOR UPDATE SKIP LOCKED
        ");
        
        $stmt->bindValue(':limit', $this->lote_tamanho, PDO::PARAM_INT);
        $stmt->execute();
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * PROCESSAR ITEM DA FILA
     */
    private function processarItem($item) {
        $this->pdo->beginTransaction();
        
        try {
            // Marcar como em processamento
            $this->atualizarStatus($item['cdfila'], 'processando');
            
            $dados = json_decode($item['dados_json'], true);
            
            // Executar operação específica
            switch ($item['tipo_operacao']) {
                case 'INSERT':
                    $resultado = $this->executarInsert($item['cdintegracao'], $dados);
                    break;
                case 'UPDATE':
                    $resultado = $this->executarUpdate($item['cdintegracao'], $dados);
                    break;
                case 'DELETE':
                    $resultado = $this->executarDelete($item['cdintegracao'], $dados);
                    break;
                default:
                    throw new Exception("Operação desconhecida: {$item['tipo_operacao']}");
            }
            
            // Se sucesso, commit
            $this->pdo->commit();
            $this->atualizarStatus($item['cdfila'], 'concluido');
            
        } catch (Exception $e) {
            $this->pdo->rollBack();
            
            // Incrementar tentativas
            $qttenativas = $item['qttenativas'] + 1;
            
            if ($qttenativas >= 5) {
                // Máximo de tentativas atingido
                $this->atualizarStatus($item['cdfila'], 'erro', $e->getMessage());
                // Notificar administrador
                $this->notificarErro($item, $e->getMessage());
            } else {
                // Reagendar para reprocessamento
                $stmt = $this->pdo->prepare("
                    UPDATE back.integracao_fila
                    SET flstatus = 'pendente', qttenativas = :qt, dthultima_tentativa = NOW()
                    WHERE cdfila = :id
                ");
                $stmt->execute([
                    ':qt' => $qttenativas,
                    ':id' => $item['cdfila']
                ]);
            }
        }
    }
    
    /**
     * EXECUTAR INSERT
     */
    private function executarInsert($cdintegracao, $dados) {
        $mapper = new MapeadorDados($this->pdo, $cdintegracao);
        $dest = $mapper->mapear($dados);
        $this->validarIntegridadeDest($dest);
        $stmt = $this->pdo->prepare(
            "INSERT INTO back.realizado 
            (cdempresa, cdexercicio, cdcentrocusto, cdplanocontabil, mesref, anoref, valor, florigem, id_integracao, json_origem, flcancelado, dtcriacao, cdpessoa, idlote)
            VALUES (:cdempresa, :cdexercicio, :cdcentrocusto, :cdplanocontabil, :mesref, :anoref, :valor, :florigem, :id_integracao, :json_origem, :flcancelado, NOW(), :cdpessoa, :idlote)
            RETURNING cdrealizado"
        );
        $stmt->execute([
            ':cdempresa' => $dest['cdempresa'],
            ':cdexercicio' => $dest['cdexercicio'],
            ':cdcentrocusto' => $dest['cdcentrocusto'],
            ':cdplanocontabil' => $dest['cdplanocontabil'],
            ':mesref' => $dest['mesref'],
            ':anoref' => $dest['anoref'],
            ':valor' => $dest['valor'],
            ':florigem' => 2,
            ':id_integracao' => $dest['id_integracao'],
            ':json_origem' => $dest['json_origem'],
            ':flcancelado' => 0,
            ':cdpessoa' => $dest['cdpessoa'],
            ':idlote' => $dest['idlote']
        ]);
        $cdregistro = $stmt->fetchColumn();
        $this->registrarRastreamento(
            $cdintegracao,
            $dest['id_integracao'],
            'realizado',
            $cdregistro,
            hash('sha256', json_encode($dados))
        );
        return ['cdregistro' => $cdregistro];
    }
    
    /**
     * EXECUTAR UPDATE
     */
    private function executarUpdate($cdintegracao, $dados) {
        $mapper = new MapeadorDados($this->pdo, $cdintegracao);
        $dest = $mapper->mapear($dados);
        $rast = $this->obterRastreamento($cdintegracao, $dest['id_integracao']);
        if (!$rast) {
            throw new Exception('Registro para UPDATE não encontrado no rastreamento');
        }
        $this->validarIntegridadeDest($dest);
        $stmt = $this->pdo->prepare(
            "UPDATE back.realizado
             SET cdempresa = :cdempresa,
                 cdexercicio = :cdexercicio,
                 cdcentrocusto = :cdcentrocusto,
                 cdplanocontabil = :cdplanocontabil,
                 mesref = :mesref,
                 anoref = :anoref,
                 valor = :valor,
                 dtcriacao = dtcriacao
             WHERE cdrealizado = :id"
        );
        $stmt->execute([
            ':cdempresa' => $dest['cdempresa'],
            ':cdexercicio' => $dest['cdexercicio'],
            ':cdcentrocusto' => $dest['cdcentrocusto'],
            ':cdplanocontabil' => $dest['cdplanocontabil'],
            ':mesref' => $dest['mesref'],
            ':anoref' => $dest['anoref'],
            ':valor' => $dest['valor'],
            ':id' => $rast['cdregistro_destino']
        ]);
        $this->atualizarHashRastreamento($rast['cdrast'], hash('sha256', json_encode($dados)));
        return ['sucesso' => true];
    }
    
    /**
     * EXECUTAR DELETE
     * Modelo read-only: apenas soft delete no HELM
     * O DELETE foi detectado na Fase 2 de SincronizacaoERP por ausência no ERP
     */
    private function executarDelete($cdintegracao, $dados) {
        $chave = $dados['chave_origem'] ?? $dados['id_integracao'] ?? null;
        $rast = $this->obterRastreamento($cdintegracao, $chave);
        if (!$rast) {
            throw new Exception('Registro para DELETE não encontrado no rastreamento');
        }
        $stmt = $this->pdo->prepare(
            "UPDATE back.realizado SET flcancelado = 1 WHERE cdrealizado = :id"
        );
        $stmt->execute([':id' => $rast['cdregistro_destino']]);
        $this->marcarDeletadoRastreamento($rast['cdrast']);
        return ['sucesso' => true];
    }
    
    /**
     * VALIDAR INTEGRIDADE
     */
    private function validarIntegridadeDest($dest) {
        $stmt = $this->pdo->prepare("SELECT 1 FROM back.empresa WHERE cdempresa = :id");
        $stmt->execute([':id' => $dest['cdempresa']]);
        if (!$stmt->fetchColumn()) throw new Exception('Empresa inválida');
        $stmt = $this->pdo->prepare("SELECT 1 FROM back.exercicio WHERE cdexercicio = :id");
        $stmt->execute([':id' => $dest['cdexercicio']]);
        if (!$stmt->fetchColumn()) throw new Exception('Exercício inválido');
        $stmt = $this->pdo->prepare("SELECT 1 FROM back.centro_custo WHERE cdcentrocusto = :id");
        $stmt->execute([':id' => $dest['cdcentrocusto']]);
        if (!$stmt->fetchColumn()) throw new Exception('Centro de custo inválido');
        $stmt = $this->pdo->prepare("SELECT 1 FROM back.planocontabil WHERE cdcontacontabil = :id");
        $stmt->execute([':id' => $dest['cdplanocontabil']]);
        if (!$stmt->fetchColumn()) throw new Exception('Conta contábil inválida');
        if ($dest['valor'] <= 0) throw new Exception('Valor deve ser positivo');
    }
    
    /**
     * OBTER RASTREAMENTO
     */
    private function obterRastreamento($cdintegracao, $chave_origem) {
        $stmt = $this->pdo->prepare("
            SELECT * FROM back.integracao_rastreamento
            WHERE cdintegracao = :integ AND chave_origem = :chave
            LIMIT 1
        ");
        $stmt->execute([
            ':integ' => $cdintegracao,
            ':chave' => $chave_origem
        ]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    
    /**
     * REGISTRAR RASTREAMENTO
     */
    private function registrarRastreamento($cdintegracao, $chave, $tabela, $cdregistro, $hash) {
        $stmt = $this->pdo->prepare("
            INSERT INTO back.integracao_rastreamento
            (cdintegracao, chave_origem, hash_dados, cdtabela_destino, cdregistro_destino, flstatus)
            VALUES (:integ, :chave, :hash, :tab, :cdreg, 'ativo')
        ");
        
        $stmt->execute([
            ':integ' => $cdintegracao,
            ':chave' => $chave,
            ':hash' => $hash,
            ':tab' => $tabela,
            ':cdreg' => $cdregistro
        ]);
    }
    
    /**
     * ATUALIZAR HASH NO RASTREAMENTO
     */
    private function atualizarHashRastreamento($cdrast, $novo_hash) {
        $stmt = $this->pdo->prepare("
            UPDATE back.integracao_rastreamento
            SET hash_dados = :hash, dthmodificacao = NOW()
            WHERE cdrast = :id
        ");
        
        $stmt->execute([
            ':hash' => $novo_hash,
            ':id' => $cdrast
        ]);
    }
    
    /**
     * MARCAR COMO DELETADO NO RASTREAMENTO
     */
    private function marcarDeletadoRastreamento($cdrast) {
        $stmt = $this->pdo->prepare("
            UPDATE back.integracao_rastreamento
            SET flstatus = 'deletado', dthdelecao = NOW()
            WHERE cdrast = :id
        ");
        
        $stmt->execute([':id' => $cdrast]);
    }
    
    /**
     * ATUALIZAR STATUS NA FILA
     */
    private function atualizarStatus($cdfila, $status, $mensagem = null) {
        $stmt = $this->pdo->prepare("
            UPDATE back.integracao_fila
            SET flstatus = :status, dthfim_processamento = NOW(), dsmensagem_erro = :msg
            WHERE cdfila = :id
        ");
        
        $stmt->execute([
            ':status' => $status,
            ':msg' => $mensagem,
            ':id' => $cdfila
        ]);
    }
    
    /**
     * REGISTRAR ERRO
     */
    private function registrarErro($cdfila, $mensagem) {
        $this->atualizarStatus($cdfila, 'erro', $mensagem);
    }
    
    /**
     * NOTIFICAR ADMINISTRADOR
     */
    private function notificarErro($item, $mensagem) {
        // TODO: Implementar notificação por email
        // TODO: Registrar em log de erros críticos
        
        error_log("ERRO CRÍTICO NA FILA - ID: {$item['cdfila']} - {$mensagem}");
    }
}

// EXECUTAR WORKER
if (php_sapi_name() === 'cli') {
    $processador = new FilaProcessador($pdo);
    $processador->processar();
} else {
    die("Este script deve ser executado via CLI");
}
?>
