#!/usr/bin/env php
<?php

/**
 * Script CRON pour agrégation des statistiques d'événements
 *
 * Parse les fichiers JSONL et agrège les données dans event_stats_daily
 *
 * Usage:
 *   php aggregate_event_stats.php                    # Agrège J-1
 *   php aggregate_event_stats.php --date=2025-12-20 # Agrège une date spécifique
 *   php aggregate_event_stats.php --from=2025-12-01 --to=2025-12-21  # Rattrapage plage
 *
 * À exécuter quotidiennement via crontab (1h du matin) :
 * 0 1 * * * /usr/bin/php /var/www/geosector/api/scripts/cron/aggregate_event_stats.php >> /var/www/geosector/api/logs/aggregate_stats.log 2>&1
 */

declare(strict_types=1);

// Configuration
define('LOCK_FILE', '/tmp/aggregate_event_stats.lock');
define('EVENT_LOG_DIR', __DIR__ . '/../../logs/events');

// Empêcher l'exécution multiple simultanée
if (file_exists(LOCK_FILE)) {
    $lockTime = filemtime(LOCK_FILE);
    if (time() - $lockTime > 3600) {
        unlink(LOCK_FILE);
    } else {
        die("[" . date('Y-m-d H:i:s') . "] Le processus est déjà en cours d'exécution\n");
    }
}

file_put_contents(LOCK_FILE, (string) getmypid());

register_shutdown_function(function () {
    if (file_exists(LOCK_FILE)) {
        unlink(LOCK_FILE);
    }
});

// Simuler l'environnement web pour AppConfig en CLI
if (php_sapi_name() === 'cli') {
    $hostname = gethostname();
    if (strpos($hostname, 'pra') !== false) {
        $_SERVER['SERVER_NAME'] = 'app3.geosector.fr';
    } elseif (strpos($hostname, 'rca') !== false) {
        $_SERVER['SERVER_NAME'] = 'rapp.geosector.fr';
    } else {
        $_SERVER['SERVER_NAME'] = 'dapp.geosector.fr';
    }

    $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'];
    $_SERVER['REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';

    if (!function_exists('getallheaders')) {
        function getallheaders()
        {
            return [];
        }
    }
}

// Chargement de l'environnement
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/../../src/Config/AppConfig.php';
require_once __DIR__ . '/../../src/Core/Database.php';
require_once __DIR__ . '/../../src/Services/LogService.php';

use App\Services\LogService;

/**
 * Parse les arguments CLI
 */
function parseArgs(array $argv): array
{
    $args = [
        'date' => null,
        'from' => null,
        'to' => null,
    ];

    foreach ($argv as $arg) {
        if (strpos($arg, '--date=') === 0) {
            $args['date'] = substr($arg, 7);
        } elseif (strpos($arg, '--from=') === 0) {
            $args['from'] = substr($arg, 7);
        } elseif (strpos($arg, '--to=') === 0) {
            $args['to'] = substr($arg, 5);
        }
    }

    return $args;
}

/**
 * Génère la liste des dates à traiter
 */
function getDatesToProcess(array $args): array
{
    $dates = [];

    if ($args['date']) {
        $dates[] = $args['date'];
    } elseif ($args['from'] && $args['to']) {
        $current = new DateTime($args['from']);
        $end = new DateTime($args['to']);
        while ($current <= $end) {
            $dates[] = $current->format('Y-m-d');
            $current->modify('+1 day');
        }
    } else {
        // Par défaut : J-1
        $dates[] = date('Y-m-d', strtotime('-1 day'));
    }

    return $dates;
}

/**
 * Parse un fichier JSONL et retourne les événements
 */
function parseJsonlFile(string $filePath): array
{
    $events = [];

    if (!file_exists($filePath)) {
        return $events;
    }

    $handle = fopen($filePath, 'r');
    if (!$handle) {
        return $events;
    }

    while (($line = fgets($handle)) !== false) {
        $line = trim($line);
        if (empty($line)) {
            continue;
        }

        $event = json_decode($line, true);
        if ($event && isset($event['event'])) {
            $events[] = $event;
        }
    }

    fclose($handle);
    return $events;
}

/**
 * Agrège les événements par entity_id et event_type
 */
function aggregateEvents(array $events): array
{
    $stats = [];

    foreach ($events as $event) {
        $entityId = $event['entity_id'] ?? null;
        $eventType = $event['event'] ?? 'unknown';
        $userId = $event['user_id'] ?? null;

        // Clé d'agrégation : entity_id peut être NULL (stats globales)
        $key = ($entityId ?? 'NULL') . '|' . $eventType;

        if (!isset($stats[$key])) {
            $stats[$key] = [
                'entity_id' => $entityId,
                'event_type' => $eventType,
                'count' => 0,
                'sum_amount' => 0.0,
                'user_ids' => [],
                'metadata' => [],
            ];
        }

        $stats[$key]['count']++;

        // Collecter les user_ids pour unique_users
        if ($userId !== null) {
            $stats[$key]['user_ids'][$userId] = true;
        }

        // Somme des montants pour les passages et paiements Stripe
        if (in_array($eventType, ['passage_created', 'passage_updated'])) {
            $amount = $event['amount'] ?? 0;
            $stats[$key]['sum_amount'] += (float) $amount;
        } elseif (in_array($eventType, ['stripe_payment_success', 'stripe_payment_created'])) {
            // Montant en centimes -> euros
            $amount = ($event['amount'] ?? 0) / 100;
            $stats[$key]['sum_amount'] += (float) $amount;
        }

        // Collecter metadata spécifiques
        collectMetadata($stats[$key], $event);
    }

    // Convertir user_ids en count
    foreach ($stats as &$stat) {
        $stat['unique_users'] = count($stat['user_ids']);
        unset($stat['user_ids']);

        // Finaliser les metadata
        $stat['metadata'] = finalizeMetadata($stat['metadata'], $stat['event_type']);
    }

    return $stats;
}

/**
 * Collecte les métadonnées spécifiques par type d'événement
 */
function collectMetadata(array &$stat, array $event): void
{
    $eventType = $event['event'] ?? '';

    switch ($eventType) {
        case 'login_failed':
            $reason = $event['reason'] ?? 'unknown';
            $stat['metadata']['reasons'][$reason] = ($stat['metadata']['reasons'][$reason] ?? 0) + 1;
            break;

        case 'passage_created':
            $sectorId = $event['sector_id'] ?? null;
            if ($sectorId) {
                $stat['metadata']['sectors'][$sectorId] = ($stat['metadata']['sectors'][$sectorId] ?? 0) + 1;
            }
            $paymentType = $event['payment_type'] ?? 'unknown';
            $stat['metadata']['payment_types'][$paymentType] = ($stat['metadata']['payment_types'][$paymentType] ?? 0) + 1;
            break;

        case 'stripe_payment_failed':
            $errorCode = $event['error_code'] ?? 'unknown';
            $stat['metadata']['error_codes'][$errorCode] = ($stat['metadata']['error_codes'][$errorCode] ?? 0) + 1;
            break;

        case 'stripe_terminal_error':
            $errorCode = $event['error_code'] ?? 'unknown';
            $stat['metadata']['error_codes'][$errorCode] = ($stat['metadata']['error_codes'][$errorCode] ?? 0) + 1;
            break;
    }
}

/**
 * Finalise les métadonnées (top 5, tri, etc.)
 */
function finalizeMetadata(array $metadata, string $eventType): ?array
{
    if (empty($metadata)) {
        return null;
    }

    $result = [];

    // Top 5 secteurs
    if (isset($metadata['sectors'])) {
        arsort($metadata['sectors']);
        $result['top_sectors'] = array_slice($metadata['sectors'], 0, 5, true);
    }

    // Raisons d'échec login
    if (isset($metadata['reasons'])) {
        arsort($metadata['reasons']);
        $result['failure_reasons'] = $metadata['reasons'];
    }

    // Types de paiement
    if (isset($metadata['payment_types'])) {
        arsort($metadata['payment_types']);
        $result['payment_types'] = $metadata['payment_types'];
    }

    // Codes d'erreur
    if (isset($metadata['error_codes'])) {
        arsort($metadata['error_codes']);
        $result['error_codes'] = $metadata['error_codes'];
    }

    return empty($result) ? null : $result;
}

/**
 * Insère ou met à jour les stats dans la base de données
 */
function upsertStats(PDO $db, string $date, array $stats): int
{
    $upsertedCount = 0;

    $sql = "
        INSERT INTO event_stats_daily
            (stat_date, entity_id, event_type, count, sum_amount, unique_users, metadata)
        VALUES
            (:stat_date, :entity_id, :event_type, :count, :sum_amount, :unique_users, :metadata)
        ON DUPLICATE KEY UPDATE
            count = VALUES(count),
            sum_amount = VALUES(sum_amount),
            unique_users = VALUES(unique_users),
            metadata = VALUES(metadata),
            updated_at = CURRENT_TIMESTAMP
    ";

    $stmt = $db->prepare($sql);

    foreach ($stats as $stat) {
        try {
            $stmt->execute([
                'stat_date' => $date,
                'entity_id' => $stat['entity_id'],
                'event_type' => $stat['event_type'],
                'count' => $stat['count'],
                'sum_amount' => $stat['sum_amount'],
                'unique_users' => $stat['unique_users'],
                'metadata' => $stat['metadata'] ? json_encode($stat['metadata'], JSON_UNESCAPED_UNICODE) : null,
            ]);
            $upsertedCount++;
        } catch (PDOException $e) {
            echo "  ERREUR insertion {$stat['event_type']}: " . $e->getMessage() . "\n";
        }
    }

    return $upsertedCount;
}

/**
 * Génère également les stats globales (entity_id = NULL)
 */
function generateGlobalStats(array $statsByEntity): array
{
    $globalStats = [];

    foreach ($statsByEntity as $stat) {
        $eventType = $stat['event_type'];

        if (!isset($globalStats[$eventType])) {
            $globalStats[$eventType] = [
                'entity_id' => null,
                'event_type' => $eventType,
                'count' => 0,
                'sum_amount' => 0.0,
                'unique_users' => 0,
                'metadata' => null,
            ];
        }

        $globalStats[$eventType]['count'] += $stat['count'];
        $globalStats[$eventType]['sum_amount'] += $stat['sum_amount'];
        $globalStats[$eventType]['unique_users'] += $stat['unique_users'];
    }

    return array_values($globalStats);
}

// ============================================================
// MAIN
// ============================================================

try {
    echo "[" . date('Y-m-d H:i:s') . "] Démarrage de l'agrégation des statistiques\n";

    // Initialisation
    $appConfig = AppConfig::getInstance();
    $config = $appConfig->getFullConfig();
    $environment = $appConfig->getEnvironment();

    echo "Environnement: {$environment}\n";

    Database::init($config['database']);
    $db = Database::getInstance();

    // Parser les arguments
    $args = parseArgs($argv);
    $dates = getDatesToProcess($args);

    echo "Dates à traiter: " . implode(', ', $dates) . "\n\n";

    $totalStats = 0;
    $totalEvents = 0;

    foreach ($dates as $date) {
        $jsonlFile = EVENT_LOG_DIR . '/' . $date . '.jsonl';

        echo "--- Traitement de {$date} ---\n";

        if (!file_exists($jsonlFile)) {
            echo "  Fichier non trouvé: {$jsonlFile}\n";
            continue;
        }

        $fileSize = filesize($jsonlFile);
        echo "  Fichier: " . basename($jsonlFile) . " (" . number_format($fileSize / 1024, 2) . " KB)\n";

        // Parser le fichier
        $events = parseJsonlFile($jsonlFile);
        $eventCount = count($events);
        echo "  Événements parsés: {$eventCount}\n";

        if ($eventCount === 0) {
            echo "  Aucun événement à agréger\n";
            continue;
        }

        $totalEvents += $eventCount;

        // Agréger par entity/event_type
        $stats = aggregateEvents($events);
        echo "  Agrégations par entité: " . count($stats) . "\n";

        // Générer les stats globales
        $globalStats = generateGlobalStats($stats);
        echo "  Agrégations globales: " . count($globalStats) . "\n";

        // Fusionner stats entités + globales
        $allStats = array_merge(array_values($stats), $globalStats);

        // Insérer en base
        $upserted = upsertStats($db, $date, $allStats);
        echo "  Stats insérées/mises à jour: {$upserted}\n";

        $totalStats += $upserted;
    }

    // Résumé
    echo "\n=== RÉSUMÉ ===\n";
    echo "Dates traitées: " . count($dates) . "\n";
    echo "Événements traités: {$totalEvents}\n";
    echo "Stats agrégées: {$totalStats}\n";

    // Log
    LogService::log('Agrégation des statistiques terminée', [
        'level' => 'info',
        'script' => 'aggregate_event_stats.php',
        'environment' => $environment,
        'dates_count' => count($dates),
        'events_count' => $totalEvents,
        'stats_count' => $totalStats,
    ]);

    echo "\n[" . date('Y-m-d H:i:s') . "] Agrégation terminée avec succès\n";

} catch (Exception $e) {
    $errorMsg = 'Erreur lors de l\'agrégation: ' . $e->getMessage();

    LogService::log($errorMsg, [
        'level' => 'error',
        'script' => 'aggregate_event_stats.php',
        'trace' => $e->getTraceAsString(),
    ]);

    echo "\n❌ ERREUR: {$errorMsg}\n";
    echo "Stack trace:\n" . $e->getTraceAsString() . "\n";

    exit(1);
}

exit(0);
