<?php

declare(strict_types=1);

namespace App\Controllers;

require_once __DIR__ . '/../Services/LogService.php';
require_once __DIR__ . '/../Services/EventStatsService.php';

use PDO;
use Database;
use Response;
use Session;
use App\Services\LogService;
use App\Services\EventStatsService;
use Exception;

/**
 * EventStatsController - Contrôleur des statistiques d'événements
 *
 * Endpoints pour consulter les stats agrégées et le détail des événements.
 * Accès réservé aux Admin entité (role_id = 2) et Super-admin (role_id = 1).
 */
class EventStatsController
{
    private PDO $db;
    private EventStatsService $statsService;

    /** @var array Rôles autorisés à consulter les stats */
    private const ALLOWED_ROLES = [1, 2]; // Super-admin, Admin

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->statsService = new EventStatsService();
    }

    /**
     * GET /api/events/stats/summary
     * Récupère le résumé des stats pour une date
     *
     * Query params:
     *   - date: Date (YYYY-MM-DD), défaut = aujourd'hui
     */
    public function summary(): void
    {
        try {
            if (!$this->checkAccess()) {
                return;
            }

            $entityId = $this->getEntityIdForQuery();
            $date = $_GET['date'] ?? date('Y-m-d');

            // Validation de la date
            if (!$this->isValidDate($date)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Format de date invalide (attendu: YYYY-MM-DD)',
                ], 400);
                return;
            }

            $summary = $this->statsService->getSummary($entityId, $date);

            $this->jsonWithCache([
                'status' => 'success',
                'data' => $summary,
            ], true);
        } catch (Exception $e) {
            LogService::error('Erreur lors de la récupération du résumé des stats', [
                'error' => $e->getMessage(),
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des statistiques',
            ], 500);
        }
    }

    /**
     * GET /api/events/stats/daily
     * Récupère les stats journalières sur une plage de dates
     *
     * Query params:
     *   - from: Date début (YYYY-MM-DD), requis
     *   - to: Date fin (YYYY-MM-DD), requis
     *   - events: Types d'événements (comma-separated), optionnel
     */
    public function daily(): void
    {
        try {
            if (!$this->checkAccess()) {
                return;
            }

            $entityId = $this->getEntityIdForQuery();
            $from = $_GET['from'] ?? null;
            $to = $_GET['to'] ?? null;
            $eventTypes = isset($_GET['events']) ? explode(',', $_GET['events']) : [];

            // Validation des dates
            if (!$from || !$to) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Les paramètres from et to sont requis',
                ], 400);
                return;
            }

            if (!$this->isValidDate($from) || !$this->isValidDate($to)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Format de date invalide (attendu: YYYY-MM-DD)',
                ], 400);
                return;
            }

            if ($from > $to) {
                Response::json([
                    'status' => 'error',
                    'message' => 'La date de début doit être antérieure à la date de fin',
                ], 400);
                return;
            }

            // Limiter la plage à 90 jours
            $daysDiff = (strtotime($to) - strtotime($from)) / 86400;
            if ($daysDiff > 90) {
                Response::json([
                    'status' => 'error',
                    'message' => 'La plage de dates ne peut pas dépasser 90 jours',
                ], 400);
                return;
            }

            $daily = $this->statsService->getDaily($entityId, $from, $to, $eventTypes);

            $this->jsonWithCache([
                'status' => 'success',
                'data' => [
                    'from' => $from,
                    'to' => $to,
                    'days' => $daily,
                ],
            ], true);
        } catch (Exception $e) {
            LogService::error('Erreur lors de la récupération des stats journalières', [
                'error' => $e->getMessage(),
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des statistiques',
            ], 500);
        }
    }

    /**
     * GET /api/events/stats/weekly
     * Récupère les stats hebdomadaires sur une plage de dates
     *
     * Query params:
     *   - from: Date début (YYYY-MM-DD), requis
     *   - to: Date fin (YYYY-MM-DD), requis
     *   - events: Types d'événements (comma-separated), optionnel
     */
    public function weekly(): void
    {
        try {
            if (!$this->checkAccess()) {
                return;
            }

            $entityId = $this->getEntityIdForQuery();
            $from = $_GET['from'] ?? null;
            $to = $_GET['to'] ?? null;
            $eventTypes = isset($_GET['events']) ? explode(',', $_GET['events']) : [];

            // Validation des dates
            if (!$from || !$to) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Les paramètres from et to sont requis',
                ], 400);
                return;
            }

            if (!$this->isValidDate($from) || !$this->isValidDate($to)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Format de date invalide (attendu: YYYY-MM-DD)',
                ], 400);
                return;
            }

            // Limiter la plage à 1 an
            $daysDiff = (strtotime($to) - strtotime($from)) / 86400;
            if ($daysDiff > 365) {
                Response::json([
                    'status' => 'error',
                    'message' => 'La plage de dates ne peut pas dépasser 1 an',
                ], 400);
                return;
            }

            $weekly = $this->statsService->getWeekly($entityId, $from, $to, $eventTypes);

            Response::json([
                'status' => 'success',
                'data' => [
                    'from' => $from,
                    'to' => $to,
                    'weeks' => $weekly,
                ],
            ]);
        } catch (Exception $e) {
            LogService::error('Erreur lors de la récupération des stats hebdomadaires', [
                'error' => $e->getMessage(),
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des statistiques',
            ], 500);
        }
    }

    /**
     * GET /api/events/stats/monthly
     * Récupère les stats mensuelles pour une année
     *
     * Query params:
     *   - year: Année (YYYY), défaut = année courante
     *   - events: Types d'événements (comma-separated), optionnel
     */
    public function monthly(): void
    {
        try {
            if (!$this->checkAccess()) {
                return;
            }

            $entityId = $this->getEntityIdForQuery();
            $year = isset($_GET['year']) ? (int) $_GET['year'] : (int) date('Y');
            $eventTypes = isset($_GET['events']) ? explode(',', $_GET['events']) : [];

            // Validation de l'année
            if ($year < 2020 || $year > (int) date('Y') + 1) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Année invalide',
                ], 400);
                return;
            }

            $monthly = $this->statsService->getMonthly($entityId, $year, $eventTypes);

            Response::json([
                'status' => 'success',
                'data' => [
                    'year' => $year,
                    'months' => $monthly,
                ],
            ]);
        } catch (Exception $e) {
            LogService::error('Erreur lors de la récupération des stats mensuelles', [
                'error' => $e->getMessage(),
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des statistiques',
            ], 500);
        }
    }

    /**
     * GET /api/events/stats/details
     * Récupère le détail des événements (lecture JSONL paginée)
     *
     * Query params:
     *   - date: Date (YYYY-MM-DD), requis
     *   - event: Type d'événement, optionnel
     *   - limit: Nombre max (défaut 50, max 100)
     *   - offset: Décalage pour pagination (défaut 0)
     */
    public function details(): void
    {
        try {
            if (!$this->checkAccess()) {
                return;
            }

            $entityId = $this->getEntityIdForQuery();
            $date = $_GET['date'] ?? null;
            $eventType = $_GET['event'] ?? null;
            $limit = isset($_GET['limit']) ? min((int) $_GET['limit'], 100) : 50;
            $offset = isset($_GET['offset']) ? max((int) $_GET['offset'], 0) : 0;

            // Validation de la date
            if (!$date) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Le paramètre date est requis',
                ], 400);
                return;
            }

            if (!$this->isValidDate($date)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Format de date invalide (attendu: YYYY-MM-DD)',
                ], 400);
                return;
            }

            $details = $this->statsService->getDetails($entityId, $date, $eventType, $limit, $offset);

            Response::json([
                'status' => 'success',
                'data' => $details,
            ]);
        } catch (Exception $e) {
            LogService::error('Erreur lors de la récupération du détail des événements', [
                'error' => $e->getMessage(),
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des événements',
            ], 500);
        }
    }

    /**
     * GET /api/events/stats/types
     * Récupère la liste des types d'événements disponibles
     */
    public function types(): void
    {
        try {
            if (!$this->checkAccess()) {
                return;
            }

            $types = $this->statsService->getEventTypes();

            Response::json([
                'status' => 'success',
                'data' => $types,
            ]);
        } catch (Exception $e) {
            LogService::error('Erreur lors de la récupération des types d\'événements', [
                'error' => $e->getMessage(),
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des types',
            ], 500);
        }
    }

    // ==================== MÉTHODES PRIVÉES ====================

    /**
     * Vérifie si l'utilisateur a accès aux stats
     */
    private function checkAccess(): bool
    {
        $roleId = Session::getRole();

        if (!in_array($roleId, self::ALLOWED_ROLES)) {
            Response::json([
                'status' => 'error',
                'message' => 'Accès non autorisé. Rôle Admin ou Super-admin requis.',
            ], 403);
            return false;
        }

        return true;
    }

    /**
     * Détermine l'entity_id à utiliser pour la requête
     * Super-admin (role_id = 1) peut voir toutes les entités (null)
     * Admin (role_id = 2) voit uniquement son entité
     */
    private function getEntityIdForQuery(): ?int
    {
        $roleId = Session::getRole();

        // Super-admin : accès global
        if ($roleId === 1) {
            // Permettre de filtrer par entité si spécifié
            if (isset($_GET['entity_id'])) {
                return (int) $_GET['entity_id'];
            }
            return null; // Stats globales
        }

        // Admin : uniquement son entité
        return Session::getEntityId();
    }

    /**
     * Valide le format d'une date
     */
    private function isValidDate(string $date): bool
    {
        $d = \DateTime::createFromFormat('Y-m-d', $date);
        return $d && $d->format('Y-m-d') === $date;
    }

    /**
     * Envoie une réponse JSON avec support ETag et compression gzip
     *
     * @param array $data Données à envoyer
     * @param bool $useCache Activer le cache ETag
     */
    private function jsonWithCache(array $data, bool $useCache = true): void
    {
        // Nettoyer tout buffer existant
        while (ob_get_level() > 0) {
            ob_end_clean();
        }

        // Encoder en JSON
        $jsonResponse = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        if ($jsonResponse === false) {
            Response::json([
                'status' => 'error',
                'message' => 'Erreur d\'encodage de la réponse',
            ], 500);
            return;
        }

        // Headers CORS
        $origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
        header("Access-Control-Allow-Origin: $origin");
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, Authorization, X-Requested-With');
        header('Access-Control-Expose-Headers: Content-Length, ETag');

        // Content-Type
        header('Content-Type: application/json; charset=utf-8');
        header('X-Content-Type-Options: nosniff');

        // ETag pour le cache
        if ($useCache) {
            $etag = '"' . md5($jsonResponse) . '"';
            header('ETag: ' . $etag);
            header('Cache-Control: private, max-age=300'); // 5 minutes

            // Vérifier If-None-Match
            $ifNoneMatch = $_SERVER['HTTP_IF_NONE_MATCH'] ?? '';
            if ($ifNoneMatch === $etag) {
                http_response_code(304); // Not Modified
                exit;
            }
        }

        // Compression gzip si supportée
        $supportsGzip = isset($_SERVER['HTTP_ACCEPT_ENCODING'])
            && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false;

        if ($supportsGzip && strlen($jsonResponse) > 1024) {
            $compressed = gzencode($jsonResponse, 6);
            if ($compressed !== false) {
                header('Content-Encoding: gzip');
                header('Content-Length: ' . strlen($compressed));
                http_response_code(200);
                echo $compressed;
                flush();
                exit;
            }
        }

        // Réponse non compressée
        header('Content-Length: ' . strlen($jsonResponse));
        http_response_code(200);
        echo $jsonResponse;
        flush();
        exit;
    }
}
