<?php

declare(strict_types=1);

namespace App\Controllers;

require_once __DIR__ . '/../Services/LogService.php';
require_once __DIR__ . '/../Services/EventLogService.php';
require_once __DIR__ . '/../Services/ExportService.php';
require_once __DIR__ . '/../Services/ApiService.php';
require_once __DIR__ . '/../Services/OperationDataService.php';

use PDO;
use PDOException;
use Database;
use AppConfig;
use Request;
use Response;
use Session;
use App\Services\LogService;
use App\Services\EventLogService;
use App\Services\ExportService;
use App\Services\ApiService;
use App\Services\OperationDataService;
use Exception;
use DateTime;

class OperationController {
    private PDO $db;
    private AppConfig $appConfig;

    public function __construct() {
        $this->db = Database::getInstance();
        $this->appConfig = AppConfig::getInstance();
    }

    /**
     * Récupère l'entité de l'utilisateur connecté
     * 
     * @param int $userId ID de l'utilisateur
     * @return int|null ID de l'entité ou null si non trouvé
     */
    private function getUserEntiteId(int $userId): ?int {
        try {
            $stmt = $this->db->prepare('SELECT fk_entite FROM users WHERE id = ?');
            $stmt->execute([$userId]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            return $user ? (int)$user['fk_entite'] : null;
        } catch (Exception $e) {
            LogService::log('Erreur lors de la récupération de l\'entité utilisateur', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'userId' => $userId
            ]);
            return null;
        }
    }

    /**
     * Valide les données d'une opération
     * 
     * @param array $data Données à valider
     * @param int $entiteId ID de l'entité
     * @param int|null $operationId ID de l'opération (pour update)
     * @return array|null Erreurs de validation ou null
     */
    private function validateOperationData(array $data, int $entiteId, ?int $operationId = null): ?array {
        $errors = [];

        // Validation du libellé (accepter 'name' ou 'libelle')
        $libelle = $data['libelle'] ?? $data['name'] ?? null;
        if (!$libelle || empty(trim($libelle))) {
            $errors[] = 'Le nom de l\'opération est obligatoire';
        } else {
            $libelle = trim($libelle);
            if (strlen($libelle) < 5) {
                $errors[] = 'Le nom de l\'opération doit contenir au moins 5 caractères';
            }
            if (strlen($libelle) > 75) {
                $errors[] = 'Le nom de l\'opération ne peut pas dépasser 75 caractères';
            }

            // Vérifier l'unicité du nom dans l'entité
            $sql = 'SELECT COUNT(*) as count FROM operations WHERE fk_entite = ? AND libelle = ? AND chk_active = 1';
            $params = [$entiteId, $libelle];

            if ($operationId) {
                $sql .= ' AND id != ?';
                $params[] = $operationId;
            }

            $stmt = $this->db->prepare($sql);
            $stmt->execute($params);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($result && $result['count'] > 0) {
                $errors[] = 'Une opération avec ce nom existe déjà dans votre entité';
            }
        }

        // Validation des dates
        if (!isset($data['date_deb']) || empty($data['date_deb'])) {
            $errors[] = 'La date de début est obligatoire';
        }

        if (!isset($data['date_fin']) || empty($data['date_fin'])) {
            $errors[] = 'La date de fin est obligatoire';
        }

        if (
            isset($data['date_deb']) && isset($data['date_fin']) &&
            !empty($data['date_deb']) && !empty($data['date_fin'])
        ) {

            $dateDeb = DateTime::createFromFormat('Y-m-d', $data['date_deb']);
            $dateFin = DateTime::createFromFormat('Y-m-d', $data['date_fin']);

            if (!$dateDeb) {
                $errors[] = 'Format de date de début invalide (YYYY-MM-DD attendu)';
            }

            if (!$dateFin) {
                $errors[] = 'Format de date de fin invalide (YYYY-MM-DD attendu)';
            }

            if ($dateDeb && $dateFin && $dateFin <= $dateDeb) {
                $errors[] = 'La date de fin doit être postérieure à la date de début';
            }
        }

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

    /**
     * Récupère toutes les opérations de l'entité de l'utilisateur
     */
    public function getOperations(): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $stmt = $this->db->prepare('
                SELECT id, libelle, date_deb, date_fin, chk_distinct_sectors, 
                       created_at, updated_at, chk_active
                FROM operations
                WHERE fk_entite = ?
                ORDER BY chk_active DESC, created_at DESC
            ');

            $stmt->execute([$entiteId]);
            $operations = $stmt->fetchAll(PDO::FETCH_ASSOC);

            Response::json([
                'status' => 'success',
                'operations' => $operations
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de la récupération des opérations', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des opérations'
            ], 500);
        }
    }

    /**
     * Récupère une opération spécifique par son ID
     */
    public function getOperationById(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;

            $stmt = $this->db->prepare('
                SELECT id, libelle, date_deb, date_fin, chk_distinct_sectors,
                       created_at, updated_at, chk_active
                FROM operations
                WHERE id = ? AND fk_entite = ?
            ');

            $stmt->execute([$operationId, $entiteId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            Response::json([
                'status' => 'success',
                'operation' => $operation
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de la récupération de l\'opération', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération de l\'opération'
            ], 500);
        }
    }

    /**
     * Crée une nouvelle opération avec duplication des données de l'opération active précédente
     */
    public function createOperation(): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $data = Request::getJson();

            // Validation des données
            $errors = $this->validateOperationData($data, $entiteId);
            if ($errors) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Erreurs de validation',
                    'errors' => $errors
                ], 400);
                return;
            }

            $this->db->beginTransaction();

            // Étape 1 : Récupérer l'id de l'opération active actuelle (oldOpeId)
            $stmt = $this->db->prepare('
                SELECT id FROM operations 
                WHERE fk_entite = ? AND chk_active = 1
                LIMIT 1
            ');
            $stmt->execute([$entiteId]);
            $oldOperation = $stmt->fetch(PDO::FETCH_ASSOC);
            $oldOpeId = $oldOperation ? (int)$oldOperation['id'] : null;

            LogService::log('Étape 1 : Récupération opération active', [
                'level' => 'info',
                'userId' => $userId,
                'entiteId' => $entiteId,
                'oldOpeId' => $oldOpeId
            ]);

            // Étape 2 : Créer la nouvelle opération (newOpeId) - pas encore active
            $stmt = $this->db->prepare('
                INSERT INTO operations (
                    fk_entite, libelle, date_deb, date_fin, 
                    chk_distinct_sectors, fk_user_creat, chk_active
                ) VALUES (?, ?, ?, ?, ?, ?, 0)
            ');

            $stmt->execute([
                $entiteId,
                trim($data['name']),
                $data['date_deb'],
                $data['date_fin'],
                isset($data['chk_distinct_sectors']) ? (int)$data['chk_distinct_sectors'] : 0,
                $userId
            ]);

            $newOpeId = (int)$this->db->lastInsertId();

            LogService::log('Étape 2 : Création nouvelle opération', [
                'level' => 'info',
                'userId' => $userId,
                'entiteId' => $entiteId,
                'newOpeId' => $newOpeId,
                'libelle' => trim($data['name'])
            ]);

            // Étape 3 : Insérer tous les users actifs de l'entité dans ope_users avec newOpeId
            $stmt = $this->db->prepare('
                INSERT INTO ope_users (fk_operation, fk_user, fk_role, first_name, encrypted_name, sect_name, fk_user_creat)
                SELECT ?, id, fk_role, first_name, encrypted_name, sect_name, ? 
                FROM users 
                WHERE fk_entite = ? AND chk_active = 1
            ');
            $stmt->execute([$newOpeId, $userId, $entiteId]);
            $insertedUsers = $stmt->rowCount();

            LogService::log('Étape 3 : Insertion users actifs', [
                'level' => 'info',
                'userId' => $userId,
                'entiteId' => $entiteId,
                'newOpeId' => $newOpeId,
                'insertedUsers' => $insertedUsers
            ]);

            // Étape 4 : Si oldOpeId existe, dupliquer les secteurs et données associées
            $duplicatedSectors = 0;
            $duplicatedUsersSectors = 0;
            $duplicatedPassages = 0;

            if ($oldOpeId) {
                // Étape 4.1 : Récupérer tous les secteurs de l'ancienne opération
                $stmt = $this->db->prepare('
                    SELECT id, libelle, sector, color 
                    FROM ope_sectors 
                    WHERE fk_operation = ? AND chk_active = 1
                ');
                $stmt->execute([$oldOpeId]);
                $oldSectors = $stmt->fetchAll(PDO::FETCH_ASSOC);

                foreach ($oldSectors as $oldSector) {
                    $oldSectId = (int)$oldSector['id'];

                    // Étape 4.2 : Dupliquer le secteur avec newOpeId
                    $stmt = $this->db->prepare('
                        INSERT INTO ope_sectors (fk_operation, fk_old_sector, libelle, sector, color, fk_user_creat)
                        VALUES (?, ?, ?, ?, ?, ?)
                    ');
                    $stmt->execute([
                        $newOpeId,
                        $oldSectId,
                        $oldSector['libelle'],
                        $oldSector['sector'],
                        $oldSector['color'],
                        $userId
                    ]);
                    $newSectId = (int)$this->db->lastInsertId();
                    $duplicatedSectors++;

                    // Étape 4.3 : Dupliquer les users_sectors en convertissant ancien ope_users.id → nouvel ope_users.id
                    $stmt = $this->db->prepare('
                        INSERT INTO ope_users_sectors (fk_operation, fk_user, fk_sector, fk_user_creat)
                        SELECT ?, new_ou.id, ?, ?
                        FROM ope_users_sectors ous
                        INNER JOIN ope_users old_ou ON old_ou.id = ous.fk_user AND old_ou.fk_operation = ?
                        INNER JOIN ope_users new_ou ON new_ou.fk_user = old_ou.fk_user AND new_ou.fk_operation = ?
                        WHERE ous.fk_operation = ? AND ous.fk_sector = ? AND ous.chk_active = 1
                    ');
                    $stmt->execute([$newOpeId, $newSectId, $userId, $oldOpeId, $newOpeId, $oldOpeId, $oldSectId]);
                    $duplicatedUsersSectors += $stmt->rowCount();

                    // Étape 4.4 : Dupliquer les passages en convertissant ancien ope_users.id → nouvel ope_users.id
                    $stmt = $this->db->prepare('
                        INSERT INTO ope_pass (
                            fk_operation, fk_sector, fk_user, fk_adresse, numero, rue, rue_bis, ville,
                            fk_habitat, appt, niveau, residence, gps_lat, gps_lng, encrypted_name,
                            fk_type, passed_at, montant, fk_type_reglement, chk_email_sent, chk_striped,
                            docremis, nb_passages, chk_map_create, chk_mobile, chk_synchro, anomalie,
                            fk_user_creat, chk_active
                        )
                        SELECT
                            ?, ?, new_ou.id, op.fk_adresse, op.numero, op.rue, op.rue_bis, op.ville,
                            op.fk_habitat, op.appt, op.niveau, op.residence, op.gps_lat, op.gps_lng, op.encrypted_name,
                            2, NULL, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0, ?, 1
                        FROM ope_pass op
                        INNER JOIN ope_users old_ou ON old_ou.id = op.fk_user AND old_ou.fk_operation = ?
                        INNER JOIN ope_users new_ou ON new_ou.fk_user = old_ou.fk_user AND new_ou.fk_operation = ?
                        WHERE op.fk_operation = ? AND op.fk_sector = ? AND op.chk_active = 1
                    ');
                    $stmt->execute([$newOpeId, $newSectId, $userId, $oldOpeId, $newOpeId, $oldOpeId, $oldSectId]);
                    $duplicatedPassages += $stmt->rowCount();
                }

                LogService::log('Étape 4 : Duplication données anciennes opération', [
                    'level' => 'info',
                    'userId' => $userId,
                    'oldOpeId' => $oldOpeId,
                    'newOpeId' => $newOpeId,
                    'duplicatedSectors' => $duplicatedSectors,
                    'duplicatedUsersSectors' => $duplicatedUsersSectors,
                    'duplicatedPassages' => $duplicatedPassages
                ]);
            }

            // Étape 5 : Désactiver l'ancienne opération
            if ($oldOpeId) {
                $stmt = $this->db->prepare('
                    UPDATE operations 
                    SET chk_active = 0, updated_at = NOW(), fk_user_modif = ?
                    WHERE id = ?
                ');
                $stmt->execute([$userId, $oldOpeId]);

                LogService::log('Étape 5 : Désactivation ancienne opération', [
                    'level' => 'info',
                    'userId' => $userId,
                    'oldOpeId' => $oldOpeId
                ]);
            }

            // Étape 6 : Activer la nouvelle opération
            $stmt = $this->db->prepare('
                UPDATE operations 
                SET chk_active = 1, updated_at = NOW(), fk_user_modif = ?
                WHERE id = ?
            ');
            $stmt->execute([$userId, $newOpeId]);

            LogService::log('Étape 6 : Activation nouvelle opération', [
                'level' => 'info',
                'userId' => $userId,
                'newOpeId' => $newOpeId
            ]);

            $this->db->commit();

            // Étape 7 : Préparer la réponse avec les groupes JSON
            $response = OperationDataService::prepareOperationResponse($this->db, $newOpeId, $entiteId);

            // Log de création de l'opération
            EventLogService::logOperationCreated(
                $newOpeId,
                $data['date_deb'],
                $data['date_fin']
            );

            Response::json($response, 201);
        } catch (Exception $e) {
            $this->db->rollBack();

            LogService::log('Erreur lors de la création de l\'opération', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la création de l\'opération'
            ], 500);
        }
    }


    /**
     * Met à jour une opération (uniquement l'opération active)
     */
    public function updateOperation(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;
            $data = Request::getJson();

            // Étape 1: Vérifier que l'opération existe (sans filtrer par entité d'abord)
            $stmt = $this->db->prepare('
                SELECT id, fk_entite, chk_active 
                FROM operations 
                WHERE id = ?
            ');

            $stmt->execute([$operationId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                LogService::log('Tentative de mise à jour d\'une opération inexistante', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'operationId' => $operationId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            // Étape 2: Vérifier la cohérence du fk_entite si fourni dans le JSON
            if (isset($data['fk_entite'])) {
                $receivedEntiteId = (int)$data['fk_entite'];
                $operationEntiteId = (int)$operation['fk_entite'];

                if ($receivedEntiteId !== $operationEntiteId) {
                    LogService::log('Incohérence détectée entre fk_entite reçu et celui de l\'opération', [
                        'level' => 'warning',
                        'userId' => $userId,
                        'operationId' => $operationId,
                        'receivedEntiteId' => $receivedEntiteId,
                        'operationEntiteId' => $operationEntiteId
                    ]);

                    Response::json([
                        'status' => 'error',
                        'message' => 'Incohérence détectée : l\'opération n\'appartient pas à l\'entité spécifiée'
                    ], 400);
                    return;
                }
            }

            // Étape 3: Vérifier que l'utilisateur a accès à l'entité de l'opération
            $operationEntiteId = (int)$operation['fk_entite'];
            if ($operationEntiteId !== $entiteId) {
                LogService::log('Tentative d\'accès à une opération d\'une autre entité', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'userEntiteId' => $entiteId,
                    'operationEntiteId' => $operationEntiteId,
                    'operationId' => $operationId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Vous n\'avez pas accès à cette entité'
                ], 403);
                return;
            }

            // Étape 4: Vérifier que l'opération est active
            if (!$operation['chk_active']) {
                LogService::log('Tentative de modification d\'une opération inactive', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'operationId' => $operationId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Seule l\'opération active peut être modifiée'
                ], 403);
                return;
            }

            // Validation des données
            $errors = $this->validateOperationData($data, $entiteId, $operationId);
            if ($errors) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Erreurs de validation',
                    'errors' => $errors
                ], 400);
                return;
            }

            // Mettre à jour l'opération
            $stmt = $this->db->prepare('
                UPDATE operations 
                SET libelle = ?, date_deb = ?, date_fin = ?, 
                    chk_distinct_sectors = ?, updated_at = NOW(), fk_user_modif = ?
                WHERE id = ?
            ');

            $libelle = trim($data['libelle'] ?? $data['name']);
            $stmt->execute([
                $libelle,
                $data['date_deb'],
                $data['date_fin'],
                isset($data['chk_distinct_sectors']) ? (int)$data['chk_distinct_sectors'] : 0,
                $userId,
                $operationId
            ]);

            // Log de mise à jour de l'opération
            $changes = [];
            if (isset($data['libelle']) || isset($data['name'])) {
                $changes['libelle'] = ['new' => $libelle];
            }
            if (isset($data['date_deb'])) {
                $changes['date_deb'] = ['new' => $data['date_deb']];
            }
            if (isset($data['date_fin'])) {
                $changes['date_fin'] = ['new' => $data['date_fin']];
            }
            if (isset($data['chk_distinct_sectors'])) {
                $changes['chk_distinct_sectors'] = ['new' => (int)$data['chk_distinct_sectors']];
            }

            if (!empty($changes)) {
                EventLogService::logOperationUpdated($operationId, $changes);
            }

            Response::json([
                'status' => 'success',
                'message' => 'Opération mise à jour avec succès'
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de la mise à jour de l\'opération', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la mise à jour de l\'opération'
            ], 500);
        }
    }

    /**
     * Désactive une opération
     */
    public function deleteOperation(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $operationId = (int)$id;

            // Récupérer les informations de l'utilisateur (rôle et entité)
            $stmt = $this->db->prepare('SELECT fk_entite, fk_role FROM users WHERE id = ?');
            $stmt->execute([$userId]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$user) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Utilisateur non trouvé'
                ], 404);
                return;
            }

            $userEntiteId = (int)$user['fk_entite'];
            $userRole = (int)$user['fk_role'];

            // Vérifier que l'utilisateur a un rôle > 1 (pas un simple utilisateur)
            if ($userRole <= 1) {
                LogService::log('Tentative de suppression d\'opération avec rôle insuffisant', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'userRole' => $userRole,
                    'operationId' => $operationId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Vous n\'avez pas les droits suffisants pour supprimer une opération'
                ], 403);
                return;
            }

            // Récupérer les informations de l'opération
            $stmt = $this->db->prepare('
                SELECT id, fk_entite, chk_active 
                FROM operations 
                WHERE id = ?
            ');

            $stmt->execute([$operationId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            $operationEntiteId = (int)$operation['fk_entite'];

            // Si l'utilisateur a le rôle 2, vérifier qu'il appartient à la même entité que l'opération
            if ($userRole == 2 && $userEntiteId !== $operationEntiteId) {
                LogService::log('Tentative de suppression d\'opération d\'une autre entité', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'userRole' => $userRole,
                    'userEntiteId' => $userEntiteId,
                    'operationEntiteId' => $operationEntiteId,
                    'operationId' => $operationId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Vous ne pouvez supprimer que les opérations de votre entité'
                ], 403);
                return;
            }

            // Les utilisateurs avec rôle > 2 (super admin, etc.) peuvent supprimer toutes les opérations
            // Les utilisateurs avec rôle 2 ne peuvent supprimer que les opérations de leur entité

            $operationActive = (bool)$operation['chk_active'];

            // Créer un export complet automatique avant suppression (Excel + JSON)
            try {
                $exportService = new ExportService();

                // Générer l'export Excel
                $excelFile = $exportService->generateExcelExport($operationId, $operationEntiteId);

                // Générer l'export JSON
                $jsonFile = $exportService->generateJsonExport($operationId, $operationEntiteId, 'auto');

                LogService::log('Export complet automatique créé avant suppression', [
                    'level' => 'info',
                    'userId' => $userId,
                    'operationId' => $operationId,
                    'operationActive' => $operationActive,
                    'excelFile' => $excelFile['filename'],
                    'jsonFile' => $jsonFile['filename']
                ]);
            } catch (Exception $e) {
                LogService::log('Erreur lors de l\'export complet automatique avant suppression', [
                    'level' => 'warning',
                    'error' => $e->getMessage(),
                    'operationId' => $operationId,
                    'operationActive' => $operationActive
                ]);
                // On continue même si l'export échoue
            }

            // Commencer une transaction pour supprimer toutes les données liées
            $this->db->beginTransaction();

            try {
                // 1. Supprimer les médias liés à l'opération
                $stmt = $this->db->prepare('DELETE FROM medias WHERE support = "operation" AND support_id = ?');
                $stmt->execute([$operationId]);
                $deletedMedias = $stmt->rowCount();

                // 2. Supprimer l'historique des passages (via les passages de l'opération)
                $stmt = $this->db->prepare('
                    DELETE oph FROM ope_pass_histo oph 
                    INNER JOIN ope_pass op ON oph.fk_pass = op.id 
                    WHERE op.fk_operation = ?
                ');
                $stmt->execute([$operationId]);
                $deletedPassHisto = $stmt->rowCount();

                // 3. Supprimer les passages
                $stmt = $this->db->prepare('DELETE FROM ope_pass WHERE fk_operation = ?');
                $stmt->execute([$operationId]);
                $deletedPass = $stmt->rowCount();

                // 4. Supprimer les relations utilisateurs-secteurs
                $stmt = $this->db->prepare('DELETE FROM ope_users_sectors WHERE fk_operation = ?');
                $stmt->execute([$operationId]);
                $deletedUsersSectors = $stmt->rowCount();

                // 5. Supprimer les adresses des secteurs (via les secteurs de l'opération)
                $stmt = $this->db->prepare('
                    DELETE sa FROM sectors_adresses sa 
                    INNER JOIN ope_sectors os ON sa.fk_sector = os.id 
                    WHERE os.fk_operation = ?
                ');
                $stmt->execute([$operationId]);
                $deletedSectorsAdresses = $stmt->rowCount();

                // 6. Supprimer les secteurs
                $stmt = $this->db->prepare('DELETE FROM ope_sectors WHERE fk_operation = ?');
                $stmt->execute([$operationId]);
                $deletedSectors = $stmt->rowCount();

                // 7. Supprimer les utilisateurs de l'opération
                $stmt = $this->db->prepare('DELETE FROM ope_users WHERE fk_operation = ?');
                $stmt->execute([$operationId]);
                $deletedUsers = $stmt->rowCount();

                // 8. Supprimer l'opération elle-même
                $stmt = $this->db->prepare('DELETE FROM operations WHERE id = ?');
                $stmt->execute([$operationId]);

                // Valider la transaction
                $this->db->commit();

                // Log de suppression de l'opération (suppression physique)
                EventLogService::logOperationDeleted($operationId, false);

                // Préparer la réponse selon le statut de l'opération supprimée
                $response = [
                    'status' => 'success',
                    'message' => 'Opération et toutes ses données supprimées avec succès',
                    'operation_was_active' => $operationActive,
                    'deleted_counts' => [
                        'medias' => $deletedMedias,
                        'passages_history' => $deletedPassHisto,
                        'passages' => $deletedPass,
                        'user_sectors' => $deletedUsersSectors,
                        'sectors_addresses' => $deletedSectorsAdresses,
                        'sectors' => $deletedSectors,
                        'users' => $deletedUsers
                    ]
                ];

                // Si l'opération supprimée était active, activer la dernière opération créée
                $newActiveOperationId = null;
                if ($operationActive) {
                    // Trouver la dernière opération créée de cette entité
                    $stmt = $this->db->prepare('
                        SELECT id FROM operations 
                        WHERE fk_entite = ? 
                        ORDER BY id DESC 
                        LIMIT 1
                    ');
                    $stmt->execute([$operationEntiteId]);
                    $lastOperation = $stmt->fetch(PDO::FETCH_ASSOC);

                    if ($lastOperation) {
                        $newActiveOperationId = (int)$lastOperation['id'];

                        // Activer cette opération
                        $stmt = $this->db->prepare('
                            UPDATE operations 
                            SET chk_active = 1, updated_at = NOW(), fk_user_modif = ?
                            WHERE id = ?
                        ');
                        $stmt->execute([$userId, $newActiveOperationId]);

                        LogService::log('Activation automatique de la dernière opération après suppression', [
                            'level' => 'info',
                            'userId' => $userId,
                            'entiteId' => $operationEntiteId,
                            'newActiveOperationId' => $newActiveOperationId,
                            'deletedOperationId' => $operationId
                        ]);
                    }
                }

                // Récupérer les 3 dernières opérations (dont l'active)
                $stmt = $this->db->prepare('
                    SELECT id, libelle, date_deb, date_fin, chk_distinct_sectors, 
                           created_at, updated_at, chk_active
                    FROM operations
                    WHERE fk_entite = ?
                    ORDER BY chk_active DESC, created_at DESC
                    LIMIT 3
                ');
                $stmt->execute([$operationEntiteId]);
                $operations = $stmt->fetchAll(PDO::FETCH_ASSOC);

                $response['operations'] = $operations;

                // Si une opération a été activée, récupérer ses données complètes
                if ($newActiveOperationId) {
                    // Récupérer les secteurs de la nouvelle opération active
                    $stmt = $this->db->prepare('
                        SELECT id, libelle, color, sector, created_at, updated_at, chk_active
                        FROM ope_sectors
                        WHERE fk_operation = ? AND chk_active = 1
                        ORDER BY libelle
                    ');
                    $stmt->execute([$newActiveOperationId]);
                    $sectors = $stmt->fetchAll(PDO::FETCH_ASSOC);

                    // Récupérer les passages de la nouvelle opération active
                    $stmt = $this->db->prepare('
                        SELECT 
                            p.id, p.fk_operation, p.fk_sector, p.fk_user, p.fk_adresse,
                            p.passed_at, p.fk_type, p.numero, p.rue, p.rue_bis, p.ville,
                            p.fk_habitat, p.appt, p.niveau, p.residence, p.gps_lat, p.gps_lng,
                            p.encrypted_name, p.montant, p.fk_type_reglement, p.remarque,
                            p.encrypted_email, p.encrypted_phone, p.nom_recu, p.date_recu,
                            p.chk_email_sent, p.docremis, p.date_repasser, p.nb_passages,
                            p.chk_mobile, p.anomalie, p.created_at, p.updated_at, p.chk_active
                        FROM ope_pass p
                        WHERE p.fk_operation = ? AND p.chk_active = 1
                        ORDER BY p.created_at DESC
                        LIMIT 50
                    ');
                    $stmt->execute([$newActiveOperationId]);
                    $passages = $stmt->fetchAll(PDO::FETCH_ASSOC);

                    // Déchiffrer les données sensibles des passages
                    foreach ($passages as &$passage) {
                        $passage['name'] = ApiService::decryptData($passage['encrypted_name']);
                        $passage['email'] = !empty($passage['encrypted_email']) ?
                            ApiService::decryptSearchableData($passage['encrypted_email']) : '';
                        $passage['phone'] = !empty($passage['encrypted_phone']) ?
                            ApiService::decryptData($passage['encrypted_phone']) : '';

                        // Suppression des champs chiffrés
                        unset($passage['encrypted_name'], $passage['encrypted_email'], $passage['encrypted_phone']);
                    }

                    // Récupérer les relations utilisateurs-secteurs
                    $stmt = $this->db->prepare('
                        SELECT
                            ous.id, ous.fk_operation, ous.fk_user, ous.fk_sector,
                            ous.created_at, ous.updated_at, ous.chk_active,
                            u.encrypted_name as user_name, ou.first_name as user_first_name,
                            s.libelle as sector_name
                        FROM ope_users_sectors ous
                        INNER JOIN ope_users ou ON ou.id = ous.fk_user
                        INNER JOIN users u ON u.id = ou.fk_user
                        INNER JOIN ope_sectors s ON s.id = ous.fk_sector
                        WHERE ous.fk_operation = ? AND ous.chk_active = 1
                        ORDER BY s.libelle, u.encrypted_name
                    ');
                    $stmt->execute([$newActiveOperationId]);
                    $usersSectors = $stmt->fetchAll(PDO::FETCH_ASSOC);

                    // Déchiffrer les noms d'utilisateurs
                    foreach ($usersSectors as &$userSector) {
                        $userSector['user_name'] = ApiService::decryptData($userSector['user_name']);
                        unset($userSector['encrypted_name']);
                    }

                    $response['activated_operation'] = [
                        'id' => $newActiveOperationId,
                        'sectors' => $sectors,
                        'passages' => $passages,
                        'users_sectors' => $usersSectors
                    ];
                }

                Response::json($response, 200);
            } catch (Exception $e) {
                // Annuler la transaction en cas d'erreur
                $this->db->rollBack();

                LogService::log('Erreur lors de la suppression complète de l\'opération', [
                    'level' => 'error',
                    'error' => $e->getMessage(),
                    'operationId' => $operationId,
                    'operationActive' => $operationActive,
                    'userId' => $userId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Erreur lors de la suppression complète de l\'opération'
                ], 500);
                return;
            }
        } catch (Exception $e) {
            LogService::log('Erreur lors de la suppression de l\'opération', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la suppression de l\'opération'
            ], 500);
        }
    }

    /**
     * Export Excel d'une opération (retourne directement le fichier)
     */
    public function exportExcel(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;

            // Vérifier d'abord si l'opération existe (sans filtrer par entité)
            $stmt = $this->db->prepare('
                SELECT id, libelle, chk_active, fk_entite 
                FROM operations 
                WHERE id = ?
            ');

            $stmt->execute([$operationId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                LogService::log('Opération inexistante pour export Excel', [
                    'level' => 'warning',
                    'operationId' => $operationId,
                    'userId' => $userId,
                    'entiteId' => $entiteId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            // Vérifier que l'opération appartient à l'entité de l'utilisateur
            if ((int)$operation['fk_entite'] !== $entiteId) {
                LogService::log('Tentative d\'accès à une opération d\'une autre entité pour export Excel', [
                    'level' => 'warning',
                    'operationId' => $operationId,
                    'operationEntiteId' => (int)$operation['fk_entite'],
                    'userEntiteId' => $entiteId,
                    'userId' => $userId
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Vous n\'avez pas accès à cette opération'
                ], 403);
                return;
            }

            // Paramètre optionnel pour filtrer par utilisateur
            $filterUserId = isset($_GET['user_id']) ? (int)$_GET['user_id'] : null;

            // Générer l'export Excel
            $exportService = new ExportService();
            $fileInfo = $exportService->generateExcelExport($operationId, $entiteId, $filterUserId);

            // Construire le chemin complet du fichier
            $filepath = getcwd() . '/' . $fileInfo['path'];

            if (!file_exists($filepath)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Fichier Excel non trouvé'
                ], 404);
                return;
            }

            // Nettoyer le nom de l'opération pour le nom de fichier
            $operationName = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $operation['libelle']);
            $userSuffix = $filterUserId ? "-user{$filterUserId}" : '';
            $timestamp = date('Ymd-His');
            $downloadFilename = "export-{$operationName}{$userSuffix}-{$timestamp}.xlsx";

            // Envoyer le fichier Excel directement
            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
            header('Content-Disposition: attachment; filename="' . $downloadFilename . '"');
            header('Content-Length: ' . filesize($filepath));
            header('Cache-Control: must-revalidate');
            header('Pragma: public');

            // Lire et envoyer le fichier
            readfile($filepath);

            LogService::log('Export Excel téléchargé', [
                'level' => 'info',
                'operationId' => $operationId,
                'entiteId' => $entiteId,
                'userId' => $userId,
                'filename' => $downloadFilename,
                'filterUserId' => $filterUserId
            ]);

            exit;
        } catch (Exception $e) {
            LogService::log('Erreur lors de l\'export Excel', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la génération de l\'export Excel'
            ], 500);
        }
    }

    /**
     * Export JSON d'une opération (sauvegarde)
     */
    public function exportJson(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;

            // Vérifier que l'opération existe et appartient à l'entité
            $stmt = $this->db->prepare('
                SELECT id, chk_active 
                FROM operations 
                WHERE id = ? AND fk_entite = ?
            ');

            $stmt->execute([$operationId, $entiteId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            // Type d'export (manual par défaut)
            $exportType = $_GET['type'] ?? 'manual';

            // Générer l'export JSON
            $exportService = new ExportService();
            $fileInfo = $exportService->generateJsonExport($operationId, $entiteId, $exportType);

            Response::json([
                'status' => 'success',
                'message' => 'Sauvegarde JSON générée avec succès',
                'file' => $fileInfo
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de l\'export JSON', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la génération de la sauvegarde JSON'
            ], 500);
        }
    }

    /**
     * Export complet (Excel + JSON)
     */
    public function exportFull(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;

            // Vérifier que l'opération existe et appartient à l'entité
            $stmt = $this->db->prepare('
                SELECT id, chk_active 
                FROM operations 
                WHERE id = ? AND fk_entite = ?
            ');

            $stmt->execute([$operationId, $entiteId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            $exportService = new ExportService();

            // Générer les deux exports
            $excelFile = $exportService->generateExcelExport($operationId, $entiteId);
            $jsonFile = $exportService->generateJsonExport($operationId, $entiteId, 'manual');

            Response::json([
                'status' => 'success',
                'message' => 'Export complet généré avec succès',
                'files' => [
                    'excel' => $excelFile,
                    'json' => $jsonFile
                ]
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de l\'export complet', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la génération de l\'export complet'
            ], 500);
        }
    }

    /**
     * Liste des sauvegardes d'une opération
     */
    public function getBackups(string $id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;

            // Vérifier que l'opération existe et appartient à l'entité
            $stmt = $this->db->prepare('
                SELECT id 
                FROM operations 
                WHERE id = ? AND fk_entite = ?
            ');

            $stmt->execute([$operationId, $entiteId]);
            $operation = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$operation) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Opération non trouvée'
                ], 404);
                return;
            }

            // Récupérer les fichiers d'export de cette opération
            $stmt = $this->db->prepare('
                SELECT 
                    id, fichier, file_type, file_size, description, 
                    created_at, fk_user_creat
                FROM medias 
                WHERE support = "operation" AND support_id = ? AND fk_entite = ?
                ORDER BY created_at DESC
            ');

            $stmt->execute([$operationId, $entiteId]);
            $backups = $stmt->fetchAll(PDO::FETCH_ASSOC);

            Response::json([
                'status' => 'success',
                'backups' => $backups
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de la récupération des sauvegardes', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la récupération des sauvegardes'
            ], 500);
        }
    }

    /**
     * Télécharger une sauvegarde spécifique
     */
    public function downloadBackup(string $id, string $backup_id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;
            $backupId = (int)$backup_id;

            // Vérifier que le fichier existe et appartient à l'opération/entité
            $stmt = $this->db->prepare('
                SELECT m.fichier, m.file_path, m.mime_type, m.original_name
                FROM medias m
                INNER JOIN operations o ON o.id = m.support_id
                WHERE m.id = ? AND m.support = "operation" AND m.support_id = ? 
                AND o.fk_entite = ?
            ');

            $stmt->execute([$backupId, $operationId, $entiteId]);
            $backup = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$backup) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Fichier de sauvegarde non trouvé'
                ], 404);
                return;
            }

            $filepath = getcwd() . '/' . $backup['file_path'];

            if (!file_exists($filepath)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Fichier physique non trouvé'
                ], 404);
                return;
            }

            // Envoyer le fichier
            header('Content-Type: ' . $backup['mime_type']);
            header('Content-Disposition: attachment; filename="' . $backup['original_name'] . '"');
            header('Content-Length: ' . filesize($filepath));
            readfile($filepath);
            exit;
        } catch (Exception $e) {
            LogService::log('Erreur lors du téléchargement de sauvegarde', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'backupId' => $backup_id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors du téléchargement'
            ], 500);
        }
    }

    /**
     * Supprimer une sauvegarde
     */
    public function deleteBackup(string $id, string $backup_id): void {
        try {
            $userId = Session::getUserId();
            if (!$userId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Vous devez être connecté pour effectuer cette action'
                ], 401);
                return;
            }

            $entiteId = $this->getUserEntiteId($userId);
            if (!$entiteId) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Entité non trouvée pour cet utilisateur'
                ], 404);
                return;
            }

            $operationId = (int)$id;
            $backupId = (int)$backup_id;

            // Vérifier que le fichier existe et appartient à l'opération/entité
            $stmt = $this->db->prepare('
                SELECT m.id, m.file_path
                FROM medias m
                INNER JOIN operations o ON o.id = m.support_id
                WHERE m.id = ? AND m.support = "operation" AND m.support_id = ? 
                AND o.fk_entite = ?
            ');

            $stmt->execute([$backupId, $operationId, $entiteId]);
            $backup = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$backup) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Fichier de sauvegarde non trouvé'
                ], 404);
                return;
            }

            // Supprimer le fichier physique
            $filepath = getcwd() . '/' . $backup['file_path'];
            if (file_exists($filepath)) {
                unlink($filepath);
            }

            // Supprimer l'enregistrement en base
            $stmt = $this->db->prepare('DELETE FROM medias WHERE id = ?');
            $stmt->execute([$backupId]);

            LogService::log('Suppression d\'une sauvegarde', [
                'level' => 'info',
                'userId' => $userId,
                'operationId' => $operationId,
                'backupId' => $backupId
            ]);

            Response::json([
                'status' => 'success',
                'message' => 'Sauvegarde supprimée avec succès'
            ], 200);
        } catch (Exception $e) {
            LogService::log('Erreur lors de la suppression de sauvegarde', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'operationId' => $id,
                'backupId' => $backup_id,
                'userId' => $userId ?? null
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la suppression'
            ], 500);
        }
    }
}
