<?php

declare(strict_types=1);

require_once __DIR__ . '/../Services/Security/PerformanceMonitor.php';
require_once __DIR__ . '/../Services/Security/SecurityMonitor.php';

use App\Services\Security\PerformanceMonitor;
use App\Services\Security\SecurityMonitor;

/**
 * Classe PDO étendue avec monitoring de sécurité et performance
 */
class MonitoredDatabase extends PDO {
    
    /**
     * Préparer une requête avec monitoring
     */
    public function prepare($statement, $options = []): PDOStatement|false {
        // Démarrer le chronométrage
        PerformanceMonitor::startDbQuery($statement);
        
        try {
            $stmt = parent::prepare($statement, $options);
            
            // Retourner un statement monitored
            if ($stmt !== false) {
                return new MonitoredStatement($stmt, $statement);
            }
            
            return false;
            
        } catch (PDOException $e) {
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            // Analyser l'erreur SQL
            SecurityMonitor::analyzeSQLError($e, $statement);
            
            // Re-lancer l'exception
            throw $e;
        }
    }
    
    /**
     * Exécuter une requête directement avec monitoring
     */
    public function exec($statement): int|false {
        // Démarrer le chronométrage
        PerformanceMonitor::startDbQuery($statement);
        
        try {
            $result = parent::exec($statement);
            
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            return $result;
            
        } catch (PDOException $e) {
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            // Analyser l'erreur SQL
            SecurityMonitor::analyzeSQLError($e, $statement);
            
            // Re-lancer l'exception
            throw $e;
        }
    }
    
    /**
     * Query avec monitoring
     */
    public function query($statement, $mode = null, ...$args): PDOStatement|false {
        // Démarrer le chronométrage
        PerformanceMonitor::startDbQuery($statement);
        
        try {
            // Si pas de mode spécifié, utiliser query simple
            if ($mode === null) {
                $result = parent::query($statement);
            } else {
                $result = parent::query($statement, $mode, ...$args);
            }
            
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            return $result;
            
        } catch (PDOException $e) {
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            // Analyser l'erreur SQL
            SecurityMonitor::analyzeSQLError($e, $statement);
            
            // Re-lancer l'exception
            throw $e;
        }
    }
}

/**
 * PDOStatement étendu avec monitoring
 */
class MonitoredStatement extends PDOStatement {
    
    private PDOStatement $stmt;
    private string $query;
    
    public function __construct(PDOStatement $stmt, string $query) {
        $this->stmt = $stmt;
        $this->query = $query;
    }
    
    /**
     * Exécuter avec monitoring
     */
    public function execute($params = null): bool {
        // Démarrer le chronométrage (si pas déjà fait dans prepare)
        PerformanceMonitor::startDbQuery($this->query);
        
        try {
            $result = $this->stmt->execute($params);
            
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            return $result;
            
        } catch (PDOException $e) {
            // Terminer le chronométrage
            PerformanceMonitor::endDbQuery();
            
            // Analyser l'erreur SQL
            SecurityMonitor::analyzeSQLError($e, $this->query);
            
            // Re-lancer l'exception
            throw $e;
        }
    }
    
    /**
     * Proxy vers le statement original pour toutes les autres méthodes
     */
    public function __call($method, $args) {
        return call_user_func_array([$this->stmt, $method], $args);
    }
    
    public function fetch($mode = PDO::FETCH_DEFAULT, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0): mixed {
        return $this->stmt->fetch($mode, $cursorOrientation, $cursorOffset);
    }
    
    public function fetchAll($mode = PDO::FETCH_DEFAULT, ...$args): array {
        return $this->stmt->fetchAll($mode, ...$args);
    }
    
    public function fetchColumn($column = 0): mixed {
        return $this->stmt->fetchColumn($column);
    }
    
    public function rowCount(): int {
        return $this->stmt->rowCount();
    }
    
    public function bindParam($param, &$var, $type = PDO::PARAM_STR, $maxLength = null, $driverOptions = null): bool {
        return $this->stmt->bindParam($param, $var, $type, $maxLength, $driverOptions);
    }
    
    public function bindValue($param, $value, $type = PDO::PARAM_STR): bool {
        return $this->stmt->bindValue($param, $value, $type);
    }
    
    public function closeCursor(): bool {
        return $this->stmt->closeCursor();
    }
    
    public function errorCode(): ?string {
        return $this->stmt->errorCode();
    }
    
    public function errorInfo(): array {
        return $this->stmt->errorInfo();
    }
}