<?php

class TranslationWorkerPool {
    private $workers = [];
    private $maxWorkers = 4;
    private $pdo;
    private $environment;
    private $running = true;
    
    public function __construct($pdo, $maxWorkers = null) {
        $this->pdo = $pdo;
        
        // Configurar número de workers desde environment
        if ($maxWorkers !== null) {
            $this->maxWorkers = $maxWorkers;
        } elseif (isset($_ENV['TRANSLATION_WORKERS'])) {
            $this->maxWorkers = (int)$_ENV['TRANSLATION_WORKERS'];
        }
        
        // Capturar el environment del proceso padre
        $this->environment = getenv('APP_ENVIRONMENT') ?: 'pruebas';
        
        custom_log("[WORKER_POOL] Inicializando con {$this->maxWorkers} workers");
        custom_log("[WORKER_POOL] Environment: {$this->environment}");
    }
    
    public function start() {
        // Configurar manejadores de señales para el pool
        pcntl_async_signals(true);
        pcntl_signal(SIGINT, [$this, 'handleShutdown']);
        pcntl_signal(SIGTERM, [$this, 'handleShutdown']);
        
        custom_log("[WORKER_POOL] Iniciando {$this->maxWorkers} workers...");
        
        // Iniciar workers
        for ($i = 0; $i < $this->maxWorkers; $i++) {
            $this->spawnWorker($i);
        }
        
        custom_log("[WORKER_POOL] Todos los workers iniciados");
        
        // Supervisar workers
        $this->supervise();
    }
    
    private function spawnWorker($id) {
        $pid = pcntl_fork();
        
        if ($pid == -1) {
            // Error al hacer fork
            custom_log("[WORKER_POOL] ERROR: No se pudo crear worker {$id}");
            return false;
        } elseif ($pid == 0) {
            // Proceso hijo - ejecutar worker
            try {
                // Heredar environment del padre
                putenv('APP_ENVIRONMENT=' . $this->environment);
                
                custom_log("[WORKER_{$id}] Iniciado con PID " . getmypid() . ", APP_ENVIRONMENT={$this->environment}");
                
                // IMPORTANTE: Crear nueva conexión PDO para este worker
                // No usar la conexión del padre porque se cierra por inactividad
                $maxRetries = 3;
                $retryDelay = 2;
                $pdo = null;
                
                for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
                    try {
                        require_once __DIR__ . '/../includes/db.php';
                        // $pdo se crea en db.php
                        if (isset($pdo) && $pdo !== null) {
                            custom_log("[WORKER_{$id}] Conexión a BD establecida");
                            break;
                        }
                    } catch (Exception $dbError) {
                        custom_log("[WORKER_{$id}] Intento {$attempt}/{$maxRetries} de conexión a BD falló: " . $dbError->getMessage());
                        if ($attempt < $maxRetries) {
                            sleep($retryDelay);
                        } else {
                            throw new Exception("No se pudo conectar a la BD después de {$maxRetries} intentos");
                        }
                    }
                }
                
                // Cargar dependencias necesarias
                require_once __DIR__ . '/Translate.php';
                require_once __DIR__ . '/TranslationWorker.php';
                
                // Crear y ejecutar worker con su propia conexión
                $worker = new TranslationWorker($id, $pdo);
                $worker->run();
                
            } catch (Exception $e) {
                custom_log("[WORKER_{$id}] ERROR FATAL: " . $e->getMessage());
                custom_log("[WORKER_{$id}] Stack trace: " . $e->getTraceAsString());
            }
            
            exit(0);
        } else {
            // Proceso padre - registrar worker
            $this->workers[$id] = [
                'pid' => $pid,
                'started' => time(),
                'restarts' => 0
            ];
            
            custom_log("[WORKER_POOL] Worker {$id} spawned con PID {$pid}");
            return true;
        }
    }
    
    private function supervise() {
        custom_log("[WORKER_POOL] Iniciando supervisión de workers");
        
        while ($this->running) {
            // Verificar estado de cada worker
            foreach ($this->workers as $id => $info) {
                $status = pcntl_waitpid($info['pid'], $exitStatus, WNOHANG);
                
                if ($status > 0) {
                    // Worker terminó
                    $exitCode = pcntl_wexitstatus($exitStatus);
                    custom_log("[WORKER_POOL] Worker {$id} (PID {$info['pid']}) terminó con código {$exitCode}");
                    
                    // Reiniciar worker si el pool sigue corriendo
                    if ($this->running) {
                        custom_log("[WORKER_POOL] Reiniciando worker {$id}...");
                        $this->workers[$id]['restarts']++;
                        
                        // Esperar un poco antes de reiniciar
                        sleep(1);
                        
                        $this->spawnWorker($id);
                    }
                } elseif ($status < 0) {
                    // Error al verificar estado
                    custom_log("[WORKER_POOL] Error al verificar worker {$id}");
                }
            }
            
            // Procesar señales
            pcntl_signal_dispatch();
            
            // Esperar antes de la siguiente verificación
            sleep(5);
        }
        
        custom_log("[WORKER_POOL] Supervisión detenida");
    }
    
    public function handleShutdown($signal) {
        custom_log("[WORKER_POOL] Señal {$signal} recibida, deteniendo pool...");
        $this->running = false;
        
        // Enviar señal de terminación a todos los workers
        foreach ($this->workers as $id => $info) {
            custom_log("[WORKER_POOL] Enviando SIGTERM a worker {$id} (PID {$info['pid']})");
            posix_kill($info['pid'], SIGTERM);
        }
        
        // Esperar a que todos los workers terminen
        custom_log("[WORKER_POOL] Esperando a que los workers terminen...");
        foreach ($this->workers as $id => $info) {
            pcntl_waitpid($info['pid'], $status);
            custom_log("[WORKER_POOL] Worker {$id} terminado");
        }
        
        custom_log("[WORKER_POOL] Pool detenido completamente");
    }
    
    public function getStats() {
        $stats = [
            'total_workers' => $this->maxWorkers,
            'workers' => []
        ];
        
        foreach ($this->workers as $id => $info) {
            $stats['workers'][$id] = [
                'pid' => $info['pid'],
                'uptime' => time() - $info['started'],
                'restarts' => $info['restarts']
            ];
        }
        
        return $stats;
    }
}
