<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Models\Order;
use App\Models\Withdrawal;
use App\Models\Setting;
use App\Notifications\SystemAlertNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;

/**
 * Commande de monitoring système
 * 
 * Vérifie les métriques critiques et envoie des alertes aux admins si nécessaire.
 * Exécutée toutes les heures via le scheduler.
 * 
 * Métriques surveillées:
 * - Retraits en attente (>10 ou >24h)
 * - Commandes en attente (>20 ou >48h)
 * - Espace disque (<10%)
 * - Erreurs dans les logs (>50/heure)
 * - Inscriptions suspectes (trop rapides)
 * - Paiements échoués (>30%)
 */
class MonitorSystem extends Command
{
    protected $signature = 'system:monitor 
                            {--dry-run : Afficher les alertes sans les envoyer}
                            {--force : Forcer l\'envoi même si récemment envoyé}';

    protected $description = 'Surveiller les métriques système et envoyer des alertes si nécessaire';

    /**
     * Seuils d'alerte configurables
     */
    protected array $thresholds = [
        'pending_withdrawals_count' => 10,       // Nombre max de retraits en attente
        'pending_withdrawals_hours' => 24,       // Heures max pour un retrait en attente
        'pending_orders_count' => 20,            // Nombre max de commandes en attente
        'pending_orders_hours' => 48,            // Heures max pour une commande en attente
        'disk_space_percent' => 10,              // % d'espace disque minimum
        'log_errors_per_hour' => 50,             // Erreurs max par heure dans les logs
        'registrations_per_hour' => 20,          // Inscriptions max par heure (possible spam)
        'failed_payments_percent' => 30,         // % max de paiements échoués
    ];

    protected array $alerts = [];

    public function handle()
    {
        $isDryRun = $this->option('dry-run');
        $forceAlert = $this->option('force');
        
        $this->info('=== Monitoring Système ===');
        $this->info('Date: ' . now()->format('d/m/Y H:i:s'));
        
        if ($isDryRun) {
            $this->warn('Mode simulation activé');
        }

        // Exécuter toutes les vérifications
        $this->checkPendingWithdrawals();
        $this->checkPendingOrders();
        $this->checkDiskSpace();
        $this->checkLogErrors();
        $this->checkSuspiciousRegistrations();
        $this->checkFailedPayments();
        $this->checkDatabaseHealth();

        // Afficher le résumé
        $this->displaySummary();

        // Envoyer les alertes si nécessaires
        if (!empty($this->alerts) && !$isDryRun) {
            $this->sendAlerts($forceAlert);
        }

        return Command::SUCCESS;
    }

    /**
     * Vérifier les retraits en attente
     */
    protected function checkPendingWithdrawals(): void
    {
        $pendingCount = Withdrawal::where('status', 'pending')->count();
        
        $oldestPending = Withdrawal::where('status', 'pending')
            ->orderBy('created_at', 'asc')
            ->first();
        
        $hoursOldest = $oldestPending 
            ? now()->diffInHours($oldestPending->created_at) 
            : 0;

        $this->info("📤 Retraits en attente: {$pendingCount}");
        
        if ($pendingCount > $this->thresholds['pending_withdrawals_count']) {
            $this->alerts[] = [
                'type' => 'warning',
                'title' => 'Retraits en attente élevés',
                'message' => "{$pendingCount} retraits sont en attente de traitement.",
                'action' => route('admin.withdrawals.index', ['status' => 'pending']),
            ];
            $this->error("  ⚠️ Seuil dépassé ({$pendingCount} > {$this->thresholds['pending_withdrawals_count']})");
        }

        if ($hoursOldest > $this->thresholds['pending_withdrawals_hours']) {
            $this->alerts[] = [
                'type' => 'danger',
                'title' => 'Retrait en attente depuis trop longtemps',
                'message' => "Un retrait est en attente depuis {$hoursOldest}h (max: {$this->thresholds['pending_withdrawals_hours']}h).",
                'action' => route('admin.withdrawals.show', $oldestPending->id),
            ];
            $this->error("  🚨 Retrait #{$oldestPending->id} en attente depuis {$hoursOldest}h");
        }
    }

    /**
     * Vérifier les commandes en attente
     */
    protected function checkPendingOrders(): void
    {
        $pendingCount = Order::whereIn('status', ['pending', 'processing'])->count();
        
        $oldestPending = Order::whereIn('status', ['pending', 'processing'])
            ->orderBy('created_at', 'asc')
            ->first();
        
        $hoursOldest = $oldestPending 
            ? now()->diffInHours($oldestPending->created_at) 
            : 0;

        $this->info("📦 Commandes en attente: {$pendingCount}");
        
        if ($pendingCount > $this->thresholds['pending_orders_count']) {
            $this->alerts[] = [
                'type' => 'warning',
                'title' => 'Commandes en attente élevées',
                'message' => "{$pendingCount} commandes sont en attente ou en traitement.",
                'action' => route('admin.orders.index', ['status' => 'pending']),
            ];
            $this->error("  ⚠️ Seuil dépassé ({$pendingCount} > {$this->thresholds['pending_orders_count']})");
        }

        if ($hoursOldest > $this->thresholds['pending_orders_hours']) {
            $this->alerts[] = [
                'type' => 'warning',
                'title' => 'Commande en attente depuis trop longtemps',
                'message' => "Une commande est en attente depuis {$hoursOldest}h.",
                'action' => route('admin.orders.show', $oldestPending->id),
            ];
        }
    }

    /**
     * Vérifier l'espace disque
     */
    protected function checkDiskSpace(): void
    {
        $storagePath = storage_path();
        
        if (function_exists('disk_free_space') && function_exists('disk_total_space')) {
            $freeSpace = @disk_free_space($storagePath);
            $totalSpace = @disk_total_space($storagePath);
            
            if ($freeSpace && $totalSpace) {
                $freePercent = round(($freeSpace / $totalSpace) * 100, 1);
                $freeGB = round($freeSpace / 1024 / 1024 / 1024, 2);
                
                $this->info("💾 Espace disque libre: {$freePercent}% ({$freeGB} GB)");
                
                if ($freePercent < $this->thresholds['disk_space_percent']) {
                    $this->alerts[] = [
                        'type' => 'danger',
                        'title' => 'Espace disque critique',
                        'message' => "Seulement {$freePercent}% d'espace disque libre ({$freeGB} GB). Nettoyage urgent requis.",
                        'action' => null,
                    ];
                    $this->error("  🚨 Espace disque critique!");
                }
            }
        } else {
            $this->warn("  ℹ️ Vérification espace disque non disponible");
        }
    }

    /**
     * Vérifier les erreurs dans les logs
     */
    protected function checkLogErrors(): void
    {
        $logFile = storage_path('logs/laravel.log');
        $errorCount = 0;
        
        if (file_exists($logFile)) {
            $oneHourAgo = now()->subHour();
            
            // Lire les dernières lignes du fichier log
            $handle = @fopen($logFile, 'r');
            if ($handle) {
                // Aller à la fin - 100KB
                fseek($handle, max(0, filesize($logFile) - 100000));
                
                while (($line = fgets($handle)) !== false) {
                    if (preg_match('/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\].*ERROR/', $line, $matches)) {
                        $logDate = \Carbon\Carbon::parse($matches[1]);
                        if ($logDate >= $oneHourAgo) {
                            $errorCount++;
                        }
                    }
                }
                fclose($handle);
            }
        }

        $this->info("🐛 Erreurs (dernière heure): {$errorCount}");
        
        if ($errorCount > $this->thresholds['log_errors_per_hour']) {
            $this->alerts[] = [
                'type' => 'danger',
                'title' => 'Nombreuses erreurs détectées',
                'message' => "{$errorCount} erreurs dans les logs durant la dernière heure. Vérification urgente requise.",
                'action' => null,
            ];
            $this->error("  🚨 Trop d'erreurs dans les logs!");
        }
    }

    /**
     * Vérifier les inscriptions suspectes
     */
    protected function checkSuspiciousRegistrations(): void
    {
        $recentRegistrations = User::where('created_at', '>=', now()->subHour())
            ->count();

        $this->info("👥 Inscriptions (dernière heure): {$recentRegistrations}");
        
        if ($recentRegistrations > $this->thresholds['registrations_per_hour']) {
            // Analyser les patterns suspects
            $sameIPs = User::where('created_at', '>=', now()->subHour())
                ->select('last_login_ip', DB::raw('count(*) as count'))
                ->groupBy('last_login_ip')
                ->having('count', '>', 3)
                ->get();

            if ($sameIPs->isNotEmpty()) {
                $this->alerts[] = [
                    'type' => 'warning',
                    'title' => 'Inscriptions suspectes détectées',
                    'message' => "{$recentRegistrations} inscriptions en 1h, dont plusieurs depuis les mêmes IPs. Possible tentative de spam.",
                    'action' => route('admin.users.index', ['sort' => 'created_at', 'order' => 'desc']),
                ];
                $this->warn("  ⚠️ Inscriptions suspectes depuis mêmes IPs");
            }
        }
    }

    /**
     * Vérifier le taux de paiements échoués
     */
    protected function checkFailedPayments(): void
    {
        $recentOrders = Order::where('created_at', '>=', now()->subDay());
        $totalOrders = $recentOrders->count();
        $failedOrders = (clone $recentOrders)->where('payment_status', 'failed')->count();
        
        $failedPercent = $totalOrders > 0 
            ? round(($failedOrders / $totalOrders) * 100, 1) 
            : 0;

        $this->info("💳 Paiements échoués (24h): {$failedPercent}% ({$failedOrders}/{$totalOrders})");
        
        if ($failedPercent > $this->thresholds['failed_payments_percent'] && $totalOrders >= 5) {
            $this->alerts[] = [
                'type' => 'warning',
                'title' => 'Taux de paiements échoués élevé',
                'message' => "{$failedPercent}% des paiements ont échoué dans les dernières 24h. Vérifier la configuration des passerelles de paiement.",
                'action' => null,
            ];
            $this->warn("  ⚠️ Taux d'échec de paiement élevé");
        }
    }

    /**
     * Vérifier la santé de la base de données
     */
    protected function checkDatabaseHealth(): void
    {
        try {
            $start = microtime(true);
            DB::select('SELECT 1');
            $responseTime = round((microtime(true) - $start) * 1000, 2);
            
            $this->info("🗄️ Temps de réponse DB: {$responseTime}ms");
            
            if ($responseTime > 500) {
                $this->alerts[] = [
                    'type' => 'warning',
                    'title' => 'Base de données lente',
                    'message' => "Le temps de réponse de la base de données est de {$responseTime}ms (seuil: 500ms).",
                    'action' => null,
                ];
                $this->warn("  ⚠️ Base de données lente");
            }
        } catch (\Exception $e) {
            $this->alerts[] = [
                'type' => 'danger',
                'title' => 'Erreur base de données',
                'message' => "Impossible de se connecter à la base de données: " . $e->getMessage(),
                'action' => null,
            ];
            $this->error("  🚨 Erreur de connexion DB!");
        }
    }

    /**
     * Afficher le résumé
     */
    protected function displaySummary(): void
    {
        $this->newLine();
        $this->info('=== Résumé ===');
        
        if (empty($this->alerts)) {
            $this->info('✅ Aucune alerte à signaler. Tout fonctionne correctement.');
        } else {
            $this->warn("⚠️ {$this->alerts->count()} alertes détectées:");
            foreach ($this->alerts as $alert) {
                $icon = match($alert['type']) {
                    'danger' => '🚨',
                    'warning' => '⚠️',
                    default => 'ℹ️',
                };
                $this->line("  {$icon} {$alert['title']}");
            }
        }
    }

    /**
     * Envoyer les alertes aux administrateurs
     */
    protected function sendAlerts(bool $force = false): void
    {
        // Vérifier si on a déjà envoyé une alerte récemment (éviter le spam)
        $lastAlertKey = 'last_system_alert_' . md5(json_encode(array_column($this->alerts, 'title')));
        $lastAlert = cache($lastAlertKey);
        
        if (!$force && $lastAlert && now()->diffInMinutes($lastAlert) < 60) {
            $this->info('ℹ️ Alertes similaires déjà envoyées récemment. Utilisez --force pour forcer.');
            return;
        }

        // Récupérer les admins
        $admins = User::where('role', 'admin')
            ->where('is_active', true)
            ->get();

        if ($admins->isEmpty()) {
            $this->warn('Aucun administrateur actif trouvé pour recevoir les alertes.');
            return;
        }

        // Envoyer la notification
        Notification::send($admins, new SystemAlertNotification($this->alerts));

        // Marquer comme envoyé
        cache([$lastAlertKey => now()], now()->addHours(2));

        $this->info("📧 Alertes envoyées à {$admins->count()} administrateur(s)");
        
        Log::warning('Alertes système envoyées', [
            'alerts' => $this->alerts,
            'recipients' => $admins->pluck('email')->toArray(),
        ]);
    }
}
