<?php

declare(strict_types=1);

namespace App\Services\Security;

use PDO;
use Database;

/**
 * Service de throttling pour les emails d'alerte
 * Évite le spam et groupe les alertes similaires
 */
class EmailThrottler {
    
    private static ?PDO $db = null;
    private static array $cache = [];
    
    // Configuration par défaut
    const DEFAULT_CONFIG = [
        'max_per_hour' => 10,
        'max_per_day' => 50,
        'digest_after' => 5,  // Grouper après 5 alertes similaires
        'cooldown_minutes' => 60
    ];
    
    /**
     * Vérifier si on peut envoyer un email pour ce type d'alerte
     */
    public function canSend(string $alertType, int $throttleSeconds = 0): bool {
        // Vérifier le cache en mémoire d'abord
        if (isset(self::$cache[$alertType])) {
            $lastSent = self::$cache[$alertType];
            if (time() - $lastSent < $throttleSeconds) {
                return false;
            }
        }
        
        // Vérifier en base de données
        $db = self::getDb();
        
        // Vérifier le throttle spécifique au type
        if ($throttleSeconds > 0) {
            $stmt = $db->prepare('
                SELECT COUNT(*) as count 
                FROM sec_alerts 
                WHERE alert_type = :type 
                  AND email_sent = 1 
                  AND email_sent_at >= DATE_SUB(NOW(), INTERVAL :seconds SECOND)
            ');
            
            $stmt->execute([
                'type' => $alertType,
                'seconds' => $throttleSeconds
            ]);
            
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($result['count'] > 0) {
                return false;
            }
        }
        
        // Vérifier les limites globales
        return $this->checkGlobalLimits();
    }
    
    /**
     * Vérifier les limites globales (par heure et par jour)
     */
    private function checkGlobalLimits(): bool {
        $db = self::getDb();
        $config = self::getConfig();
        
        // Vérifier limite horaire
        $hourlyStmt = $db->prepare('
            SELECT COUNT(*) as count 
            FROM sec_alerts 
            WHERE email_sent = 1 
              AND email_sent_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
        ');
        $hourlyStmt->execute();
        $hourlyCount = $hourlyStmt->fetch(PDO::FETCH_ASSOC)['count'];
        
        if ($hourlyCount >= $config['max_per_hour']) {
            error_log("Email throttler: Hourly limit reached ({$hourlyCount}/{$config['max_per_hour']})");
            return false;
        }
        
        // Vérifier limite quotidienne
        $dailyStmt = $db->prepare('
            SELECT COUNT(*) as count 
            FROM sec_alerts 
            WHERE email_sent = 1 
              AND email_sent_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)
        ');
        $dailyStmt->execute();
        $dailyCount = $dailyStmt->fetch(PDO::FETCH_ASSOC)['count'];
        
        if ($dailyCount >= $config['max_per_day']) {
            error_log("Email throttler: Daily limit reached ({$dailyCount}/{$config['max_per_day']})");
            return false;
        }
        
        return true;
    }
    
    /**
     * Enregistrer l'envoi d'un email
     */
    public function recordSent(string $alertType): void {
        // Mettre à jour le cache
        self::$cache[$alertType] = time();
        
        // Nettoyer le cache périodiquement
        if (count(self::$cache) > 100) {
            $cutoff = time() - 3600; // Garder seulement la dernière heure
            self::$cache = array_filter(self::$cache, function($time) use ($cutoff) {
                return $time > $cutoff;
            });
        }
    }
    
    /**
     * Vérifier si on doit envoyer un digest plutôt que des alertes individuelles
     */
    public function shouldSendDigest(string $alertType): bool {
        $db = self::getDb();
        $config = self::getConfig();
        
        // Compter les alertes non envoyées du même type
        $stmt = $db->prepare('
            SELECT COUNT(*) as count 
            FROM sec_alerts 
            WHERE alert_type = :type 
              AND email_sent = 0 
              AND resolved = 0
              AND created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
        ');
        
        $stmt->execute(['type' => $alertType]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $result['count'] >= $config['digest_after'];
    }
    
    /**
     * Obtenir les alertes pour un digest
     */
    public function getDigestAlerts(string $alertType, int $limit = 50): array {
        $db = self::getDb();
        
        $stmt = $db->prepare('
            SELECT * 
            FROM sec_alerts 
            WHERE alert_type = :type 
              AND email_sent = 0 
              AND resolved = 0
              AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
            ORDER BY alert_level DESC, last_seen DESC
            LIMIT :limit
        ');
        
        $stmt->bindValue('type', $alertType, PDO::PARAM_STR);
        $stmt->bindValue('limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    
    /**
     * Marquer les alertes comme envoyées dans un digest
     */
    public function markDigestSent(array $alertIds): void {
        if (empty($alertIds)) {
            return;
        }
        
        $db = self::getDb();
        $placeholders = implode(',', array_fill(0, count($alertIds), '?'));
        
        $stmt = $db->prepare("
            UPDATE sec_alerts 
            SET email_sent = 1, 
                email_sent_at = NOW(),
                notes = CONCAT(IFNULL(notes, ''), '\nInclu dans digest email')
            WHERE id IN ({$placeholders})
        ");
        
        $stmt->execute($alertIds);
    }
    
    /**
     * Obtenir les statistiques de throttling
     */
    public function getStats(): array {
        $db = self::getDb();
        $config = self::getConfig();
        
        // Emails envoyés dans l'heure
        $hourlyStmt = $db->prepare('
            SELECT COUNT(*) as count 
            FROM sec_alerts 
            WHERE email_sent = 1 
              AND email_sent_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
        ');
        $hourlyStmt->execute();
        $hourlyCount = $hourlyStmt->fetch(PDO::FETCH_ASSOC)['count'];
        
        // Emails envoyés dans la journée
        $dailyStmt = $db->prepare('
            SELECT COUNT(*) as count 
            FROM sec_alerts 
            WHERE email_sent = 1 
              AND email_sent_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)
        ');
        $dailyStmt->execute();
        $dailyCount = $dailyStmt->fetch(PDO::FETCH_ASSOC)['count'];
        
        // Alertes en attente
        $pendingStmt = $db->prepare('
            SELECT 
                alert_type,
                COUNT(*) as count,
                MIN(first_seen) as oldest
            FROM sec_alerts 
            WHERE email_sent = 0 
              AND resolved = 0
            GROUP BY alert_type
        ');
        $pendingStmt->execute();
        $pending = $pendingStmt->fetchAll(PDO::FETCH_ASSOC);
        
        return [
            'hourly' => [
                'sent' => $hourlyCount,
                'limit' => $config['max_per_hour'],
                'remaining' => max(0, $config['max_per_hour'] - $hourlyCount)
            ],
            'daily' => [
                'sent' => $dailyCount,
                'limit' => $config['max_per_day'],
                'remaining' => max(0, $config['max_per_day'] - $dailyCount)
            ],
            'pending_alerts' => $pending,
            'cache_size' => count(self::$cache),
            'config' => $config
        ];
    }
    
    /**
     * Réinitialiser les compteurs (utile pour les tests)
     */
    public function reset(): void {
        self::$cache = [];
        
        // Optionnel : réinitialiser les flags en DB
        $db = self::getDb();
        $stmt = $db->prepare('
            UPDATE sec_alerts 
            SET email_sent = 0, email_sent_at = NULL 
            WHERE email_sent_at < DATE_SUB(NOW(), INTERVAL 1 DAY)
        ');
        $stmt->execute();
    }
    
    /**
     * Obtenir la configuration
     */
    private static function getConfig(): array {
        // Essayer de charger depuis AppConfig si disponible
        try {
            if (class_exists('AppConfig')) {
                $appConfig = \AppConfig::getInstance();
                // AppConfig n'a pas de méthode get() générique
                // On pourrait ajouter une configuration dans getCurrentConfig() si nécessaire
                // Pour l'instant, utiliser la configuration par défaut
            }
        } catch (\Exception $e) {
            // Utiliser config par défaut
        }
        
        return self::DEFAULT_CONFIG;
    }
    
    /**
     * Obtenir l'instance de base de données
     */
    private static function getDb(): PDO {
        if (self::$db === null) {
            self::$db = Database::getInstance();
        }
        return self::$db;
    }
}