<?php

declare(strict_types=1);

namespace App\Controllers;

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
use PDO;
use PDOException;
use Database;
use AppConfig;
use Request;
use Response;
use Session;
use App\Services\LogService;
use App\Services\ApiService;
use App\Services\EventLogService;

require_once __DIR__ . '/../Services/LogService.php';
require_once __DIR__ . '/../Services/EventLogService.php';
require_once __DIR__ . '/../Services/ApiService.php';
require_once __DIR__ . '/EntiteController.php';
require_once __DIR__ . '/../Services/Security/SecurityMonitor.php';

use App\Services\Security\SecurityMonitor;

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

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

    public function login(): void {
        try {
            $data = Request::getJson();

            if (!isset($data['username'], $data['password'], $data['type'])) {
                LogService::log('Tentative de connexion GeoSector échouée : données manquantes', [
                    'level' => 'warning',
                    'username' => $data['username'] ?? 'non fourni'
                ]);
                Response::json(['error' => 'Nom d\'utilisateur et mot de passe requis'], 400);
                return;
            }

            $interface = trim($data['type']);
            $username = trim($data['username']);
            $encryptedUsername = ApiService::encryptSearchableData($username);

            // Récupérer le type d'utilisateur
            // user accessible aux fk_role=1 ET fk_role=2 (membres + admins amicale)
            // admin accessible uniquement aux fk_role>1 (admins amicale + super-admins)
            $roleCondition = ($interface === 'user') ? 'AND fk_role IN (1, 2)' : 'AND fk_role>1';

            // Requête optimisée: on récupère l'utilisateur et son entité en une seule fois avec LEFT JOIN
            $stmt = $this->db->prepare(
                'SELECT 
                    u.id, u.encrypted_email, u.encrypted_user_name, u.encrypted_name, u.user_pass_hash, 
                    u.first_name, u.fk_role, u.fk_entite, u.fk_titre, u.chk_active, u.sect_name,
                    u.date_naissance, u.date_embauche, u.encrypted_phone, u.encrypted_mobile,
                    e.id AS entite_id, e.encrypted_name AS entite_encrypted_name, 
                    e.adresse1, e.code_postal, e.ville, e.gps_lat, e.gps_lng, e.chk_active AS entite_chk_active
                FROM users u
                LEFT JOIN entites e ON u.fk_entite = e.id
                WHERE u.encrypted_user_name = ? AND u.chk_active != 0 ' . $roleCondition
            );
            $stmt->execute([$encryptedUsername]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$user) {
                // Enregistrer la tentative échouée
                $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
                $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
                SecurityMonitor::recordFailedLogin($clientIp, $username, 'user_not_found', $userAgent);

                EventLogService::logLoginFailed($username, 'user_not_found', 1);
                Response::json(['error' => 'Identifiants invalides'], 401);
                return;
            }

            // Vérification du mot de passe
            $passwordValid = password_verify($data['password'], $user['user_pass_hash']);

            if (!$passwordValid) {
                // Enregistrer la tentative échouée
                $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
                $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null;
                SecurityMonitor::recordFailedLogin($clientIp, $username, 'invalid_password', $userAgent);

                EventLogService::logLoginFailed($username, 'invalid_password', 1);
                Response::json(['error' => 'Identifiants invalides'], 401);
                return;
            }

            // Vérifier si l'utilisateur a une entité et si elle est active
            if (!empty($user['fk_entite']) && (!isset($user['entite_chk_active']) || $user['entite_chk_active'] != 1)) {
                EventLogService::logLoginFailed($username, 'account_inactive', 1);
                Response::json([
                    'status' => 'error',
                    'message' => 'Votre amicale n\'est pas activée. Veuillez contacter votre administrateur.'
                ], 403);
                return;
            }

            // Mise à jour de last_login et activation du compte si nécessaire
            $updateQuery = 'UPDATE users SET 
            updated_at = NOW()' .
                ($user['chk_active'] == -1 ? ', chk_active = 1' : '') .
                ($user['chk_active'] == 2 ? ', chk_active = 1' : '') .
                ' WHERE id = ?';

            $updateStmt = $this->db->prepare($updateQuery);
            $updateStmt->execute([$user['id']]);

            // Déchiffrement du nom
            $decryptedName = ApiService::decryptData($user['encrypted_name']);

            // Déchiffrement de l'email si disponible
            $email = '';
            if (!empty($user['encrypted_email'])) {
                $email = ApiService::decryptSearchableData($user['encrypted_email']);

                // Si le déchiffrement échoue, renvoyer une erreur
                if (empty($email)) {
                    LogService::log('Déchiffrement email échoué', [
                        'level' => 'error',
                        'message' => 'Déchiffrement de l\'email échoué',
                        'encrypted_email' => $user['encrypted_email'],
                        'user_id' => $user['id']
                    ]);

                    Response::json([
                        'status' => 'error',
                        'message' => 'Erreur de déchiffrement de l\'email. Exécutez le script de migration pour résoudre ce problème.',
                        'debug_info' => [
                            'encrypted_email' => $user['encrypted_email'],
                            'user_id' => $user['id']
                        ]
                    ], 500);
                    return;
                }
            }

            // Création de la session
            $sessionData = [
                'id' => $user['id'],
                'username' => $username,
                'email' => $email,
                'name' => $decryptedName,
                'first_name' => $user['first_name'] ?? '',
                'fk_role' => $user['fk_role'] ?? '0',
                'fk_entite' => $user['fk_entite'] ?? '0',
            ];
            Session::login($sessionData);
            
            // Vérifier et exécuter l'initialisation des contours départementaux pour d6soft
            if ($username === 'd6soft') {
                require_once __DIR__ . '/../../scripts/init_departements_contours.php';
                $initLog = \DepartementContoursInitializer::runIfNeeded($this->db, $username);
                
                if ($initLog !== null) {
                    // Logger l'initialisation
                    LogService::log('Initialisation des contours départementaux', [
                        'level' => 'info',
                        'username' => $username,
                        'log_count' => count($initLog)
                    ]);
                    
                    // Logger aussi les dernières lignes du log pour diagnostic
                    $lastLines = array_slice($initLog, -5);
                    foreach ($lastLines as $line) {
                        if (strpos($line, '✗') !== false || strpos($line, 'terminé') !== false) {
                            LogService::log('Import contours: ' . $line, [
                                'level' => 'info',
                                'username' => $username
                            ]);
                        }
                    }
                }
            }

            // Préparation des données utilisateur pour la réponse (uniquement les champs du user)
            $userData = [
                'id' => $user['id'],
                'fk_entite' => $user['fk_entite'] ?? null,
                'fk_role' => $user['fk_role'] ?? '0',
                'fk_titre' => $user['fk_titre'] ?? null,
                'first_name' => $user['first_name'] ?? '',
                'sect_name' => $user['sect_name'] ?? '',
                'date_naissance' => $user['date_naissance'] ?? null,
                'date_embauche' => $user['date_embauche'] ?? null,
                'username' => $username,
                'name' => $decryptedName
            ];

            // Déchiffrement du téléphone
            if (!empty($user['encrypted_phone'])) {
                $userData['phone'] = ApiService::decryptData($user['encrypted_phone']);
            } else {
                $userData['phone'] = '';
            }

            // Déchiffrement du mobile
            if (!empty($user['encrypted_mobile'])) {
                $userData['mobile'] = ApiService::decryptData($user['encrypted_mobile']);
            } else {
                $userData['mobile'] = '';
            }

            // L'email est déjà déchiffré plus haut dans le code
            $userData['email'] = $email;

            // Suivant l'interface et le role de l'utilisateur, on lui charge toutes ses données utiles

            // operations : 
            // Si $interface='user' : on ne récupère que la dernière opération active
            // Si $interface='admin' et si $user['fk_role']=2 : on récupère les 3 dernières opérations dont celle active
            // Dans tous les autres cas, operations: []

            // secteurs :
            // On récupère les secteurs de l'opération active trouvée, sinon secteurs: []

            // passages :
            // On récupère les passages du ou des secteurs trouvés, sinon passages: []

            // users_sectors :
            // On récupère les users affectés aux secteurs partagés de l'utilisateur, si pas de secteurs, users_passages: []

            // clients :
            // Si $interface="admin" et si $user['fk_role']=9
            // On récupère les entités au complet sauf la entite.id=1 dans un group clients contenant id, name, adresse1, adresse2, code_postal, ville, fk_region, lib_region, fk_type, phone, mobile, email, gps_lat, gps_lng, chk_active

            // Suivant l'interface et le role de l'utilisateur, on lui charge toutes ses données utiles
            $operationsData = [];
            $sectorsData = [];
            $passagesData = [];
            $usersSectorsData = [];

            // 1. Récupération des opérations selon les critères
            $operationLimit = 0;
            $activeOperationOnly = false;

            if ($interface === 'user') {
                // Interface utilisateur : seulement la dernière opération active
                $operationLimit = 1;
                $activeOperationOnly = true;
            } elseif ($interface === 'admin' && $user['fk_role'] == 2) {
                // Interface admin avec rôle 2 : les 3 dernières opérations dont l'active
                $operationLimit = 3;
            } elseif ($interface === 'admin' && $user['fk_role'] > 2) {
                // Interface admin avec rôle > 2 : les 10 dernières opérations dont l'active
                $operationLimit = 10;
            } else {
                // Autres cas : pas d'opérations
                $operationLimit = 0;
            }

            if ($operationLimit > 0 && !empty($user['fk_entite'])) {
                $operationQuery = "SELECT id, fk_entite, libelle, date_deb, date_fin, chk_active 
                                   FROM operations 
                                   WHERE fk_entite = ?";

                if ($activeOperationOnly) {
                    $operationQuery .= " AND chk_active = 1";
                }

                $operationQuery .= " ORDER BY id DESC LIMIT " . $operationLimit;

                $operationStmt = $this->db->prepare($operationQuery);
                $operationStmt->execute([$user['fk_entite']]);
                $operations = $operationStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($operations)) {
                    // Formater les données des opérations
                    foreach ($operations as $operation) {
                        $operationsData[] = [
                            'id' => $operation['id'],
                            'fk_entite' => $operation['fk_entite'],
                            'libelle' => $operation['libelle'],
                            'date_deb' => $operation['date_deb'],
                            'date_fin' => $operation['date_fin'],
                            'chk_active' => $operation['chk_active']
                        ];
                    }

                    // Récupérer l'ID de l'opération active (première opération retournée)
                    $activeOperationId = $operations[0]['id'];

                    // Récupérer ope_user_id pour l'utilisateur connecté et l'opération active
                    $opeUserStmt = $this->db->prepare(
                        'SELECT id FROM ope_users WHERE fk_user = ? AND fk_operation = ?'
                    );
                    $opeUserStmt->execute([$user['id'], $activeOperationId]);
                    $opeUser = $opeUserStmt->fetch(PDO::FETCH_ASSOC);

                    if ($opeUser) {
                        $userData['ope_user_id'] = $opeUser['id'];
                    } else {
                        $userData['ope_user_id'] = null;
                    }

                    // 2. Récupérer les secteurs selon l'interface et le rôle
                    if ($interface === 'user') {
                        // Interface utilisateur : seulement les secteurs affectés à l'utilisateur
                        // Utiliser ope_user_id au lieu de users.id
                        $opeUserId = $userData['ope_user_id'];
                        if ($opeUserId) {
                            $sectorsStmt = $this->db->prepare(
                                'SELECT s.id, s.libelle, s.color, s.sector
                                FROM ope_sectors s
                                JOIN ope_users_sectors us ON s.id = us.fk_sector
                                WHERE us.fk_operation = ? AND us.fk_user = ? AND us.chk_active = 1 AND s.chk_active = 1'
                            );
                            $sectorsStmt->execute([$activeOperationId, $opeUserId]);
                        }
                    } elseif ($interface === 'admin' && $user['fk_role'] == 2) {
                        // Interface admin avec rôle 2 : tous les secteurs distincts de l'opération
                        $sectorsStmt = $this->db->prepare(
                            'SELECT DISTINCT s.id, s.libelle, s.color, s.sector
                            FROM ope_sectors s 
                            WHERE s.fk_operation = ? AND s.chk_active = 1'
                        );
                        $sectorsStmt->execute([$activeOperationId]);
                    } else {
                        // Autres cas : pas de secteurs
                        $sectors = [];
                        $sectorsData = [];
                    }

                    // Récupération des secteurs si une requête a été préparée
                    if (isset($sectorsStmt)) {
                        $sectors = $sectorsStmt->fetchAll(PDO::FETCH_ASSOC);
                    } else {
                        $sectors = [];
                    }

                    if (!empty($sectors)) {
                        $sectorsData = $sectors;

                        // 3. Récupérer les passages selon l'interface et le rôle
                        if ($interface === 'user' && !empty($sectors)) {
                            // Interface utilisateur : passages de l'utilisateur + passages à finaliser sur ses secteurs
                            // Utiliser ope_user_id au lieu de users.id
                            $opeUserId = $userData['ope_user_id'];
                            $sectorIds = array_column($sectors, 'id');
                            $sectorIdsString = implode(',', $sectorIds);

                            if (!empty($sectorIdsString) && $opeUserId) {
                                $passagesStmt = $this->db->prepare(
                                    "SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau,
                                    gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages
                                    FROM ope_pass
                                    WHERE fk_operation = ?
                                    AND chk_active = 1
                                    AND (
                                        (fk_user = ?)  -- TOUS les passages de l'utilisateur
                                        OR
                                        (fk_sector IN ($sectorIdsString) AND fk_type = 2 AND fk_user != ?)  -- Passages type 2 des autres sur ses secteurs
                                    )
                                    ORDER BY passed_at DESC"
                                );
                                $passagesStmt->execute([$activeOperationId, $opeUserId, $opeUserId]);
                            }
                        } elseif ($interface === 'admin' && $user['fk_role'] == 2) {
                            // Interface admin avec rôle 2 : tous les passages de l'opération
                            $passagesStmt = $this->db->prepare(
                                "SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau, 
                                gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages 
                                FROM ope_pass 
                                WHERE fk_operation = ? AND chk_active = 1"
                            );
                            $passagesStmt->execute([$activeOperationId]);
                        } else {
                            // Autres cas : pas de passages
                            $passages = [];
                            $passagesData = [];
                        }

                        // Récupération des passages si une requête a été préparée
                        if (isset($passagesStmt)) {
                            $passages = $passagesStmt->fetchAll(PDO::FETCH_ASSOC);
                        } else {
                            $passages = [];
                        }

                        if (!empty($passages)) {
                            // Déchiffrer les données sensibles
                            foreach ($passages as &$passage) {
                                // Déchiffrement du nom
                                $passage['name'] = '';
                                if (!empty($passage['encrypted_name'])) {
                                    $passage['name'] = ApiService::decryptData($passage['encrypted_name']);
                                }
                                unset($passage['encrypted_name']);

                                // Déchiffrement de l'email
                                $passage['email'] = '';
                                if (!empty($passage['encrypted_email'])) {
                                    $decryptedEmail = ApiService::decryptSearchableData($passage['encrypted_email']);
                                    if ($decryptedEmail) {
                                        $passage['email'] = $decryptedEmail;
                                    }
                                }
                                unset($passage['encrypted_email']);

                                // Déchiffrement du téléphone
                                $passage['phone'] = '';
                                if (!empty($passage['encrypted_phone'])) {
                                    $passage['phone'] = ApiService::decryptData($passage['encrypted_phone']);
                                }
                                unset($passage['encrypted_phone']);
                            }
                            $passagesData = $passages;
                        }

                        // 4. Récupérer les utilisateurs des secteurs partagés
                        if (($interface === 'user' || ($interface === 'admin' && $user['fk_role'] == 2)) && !empty($sectors)) {
                            $sectorIds = array_column($sectors, 'id');
                            $sectorIdsString = implode(',', $sectorIds);

                            if (!empty($sectorIdsString)) {
                                $usersSectorsStmt = $this->db->prepare(
                                    "SELECT DISTINCT u.id as user_id, ou.id as ope_user_id, ou.first_name, u.encrypted_name, u.sect_name, us.fk_sector
                                    FROM users u
                                    JOIN ope_users ou ON u.id = ou.fk_user
                                    JOIN ope_users_sectors us ON ou.id = us.fk_user AND ou.fk_operation = us.fk_operation
                                    WHERE us.fk_sector IN ($sectorIdsString)
                                    AND us.fk_operation = ?
                                    AND us.chk_active = 1
                                    AND u.chk_active = 1
                                    AND u.id != ?" // Exclure l'utilisateur connecté
                                );
                                $usersSectorsStmt->execute([$activeOperationId, $user['id']]);
                                $usersSectors = $usersSectorsStmt->fetchAll(PDO::FETCH_ASSOC);

                                if (!empty($usersSectors)) {
                                    // Déchiffrer les noms des utilisateurs
                                    foreach ($usersSectors as &$userSector) {
                                        if (!empty($userSector['encrypted_name'])) {
                                            $userSector['name'] = ApiService::decryptData($userSector['encrypted_name']);
                                            unset($userSector['encrypted_name']);
                                        }
                                    }
                                    $usersSectorsData = $usersSectors;
                                }
                            }
                        } else {
                            // Autres cas : pas d'utilisateurs de secteurs
                            $usersSectorsData = [];
                        }
                    }
                }
            }

            // 5. Section clients gérée plus bas pour les super-administrateurs

            // 6. Récupérer les membres (users de l'entité du user) si nécessaire
            if ($interface === 'admin' && $user['fk_role'] == 2 && !empty($user['fk_entite'])) {
                // Si on a une opération active, on récupère aussi ope_user_id
                if (isset($activeOperationId)) {
                    $membresStmt = $this->db->prepare(
                        'SELECT u.id, u.fk_role, u.fk_entite, u.fk_titre, u.encrypted_name, u.first_name, u.sect_name,
                         u.encrypted_user_name, u.encrypted_phone, u.encrypted_mobile, u.encrypted_email,
                         u.date_naissance, u.date_embauche, u.chk_active, ou.id as ope_user_id
                         FROM users u
                         LEFT JOIN ope_users ou ON u.id = ou.fk_user AND ou.fk_operation = ?
                         WHERE u.fk_entite = ?'
                    );
                    $membresStmt->execute([$activeOperationId, $user['fk_entite']]);
                } else {
                    $membresStmt = $this->db->prepare(
                        'SELECT id, fk_role, fk_entite, fk_titre, encrypted_name, first_name, sect_name,
                         encrypted_user_name, encrypted_phone, encrypted_mobile, encrypted_email,
                         date_naissance, date_embauche, chk_active
                         FROM users
                         WHERE fk_entite = ?'
                    );
                    $membresStmt->execute([$user['fk_entite']]);
                }
                $membres = $membresStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($membres)) {
                    $membresData = [];

                    foreach ($membres as $membre) {
                        $membreItem = [
                            'id' => $membre['id'],
                            'ope_user_id' => $membre['ope_user_id'] ?? null,
                            'fk_role' => $membre['fk_role'],
                            'fk_entite' => $membre['fk_entite'],
                            'fk_titre' => $membre['fk_titre'],
                            'first_name' => $membre['first_name'] ?? '',
                            'sect_name' => $membre['sect_name'] ?? '',
                            'date_naissance' => $membre['date_naissance'] ?? null,
                            'date_embauche' => $membre['date_embauche'] ?? null,
                            'chk_active' => $membre['chk_active']
                        ];

                        // Déchiffrement du nom
                        if (!empty($membre['encrypted_name'])) {
                            $membreItem['name'] = ApiService::decryptData($membre['encrypted_name']);
                        } else {
                            $membreItem['name'] = '';
                        }

                        // Déchiffrement du nom d'utilisateur
                        if (!empty($membre['encrypted_user_name'])) {
                            $membreItem['username'] = ApiService::decryptSearchableData($membre['encrypted_user_name']);
                        } else {
                            $membreItem['username'] = '';
                        }

                        // Déchiffrement du téléphone
                        if (!empty($membre['encrypted_phone'])) {
                            $membreItem['phone'] = ApiService::decryptData($membre['encrypted_phone']);
                        } else {
                            $membreItem['phone'] = '';
                        }

                        // Déchiffrement du mobile
                        if (!empty($membre['encrypted_mobile'])) {
                            $membreItem['mobile'] = ApiService::decryptData($membre['encrypted_mobile']);
                        } else {
                            $membreItem['mobile'] = '';
                        }

                        // Déchiffrement de l'email
                        if (!empty($membre['encrypted_email'])) {
                            $decryptedEmail = ApiService::decryptSearchableData($membre['encrypted_email']);
                            if ($decryptedEmail) {
                                $membreItem['email'] = $decryptedEmail;
                            }
                        } else {
                            $membreItem['email'] = '';
                        }

                        $membresData[] = $membreItem;
                    }

                    // Les membres seront ajoutés à la racine de la réponse plus tard
                    // (après la préparation de la réponse)
                }
            }

            // 7. Récupérer les amicales selon le rôle de l'utilisateur
            $amicalesData = [];

            if (!empty($user['fk_entite'])) {
                if ($user['fk_role'] <= 2) {
                    // User normal ou admin avec fk_role=2: uniquement son amicale
                    $amicaleStmt = $this->db->prepare(
                        'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
            e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
            e.encrypted_email as email, e.gps_lat, e.gps_lng,
            sa.stripe_account_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
            e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass,
            sa.stripe_location_id
            FROM entites e
            LEFT JOIN x_regions r ON e.fk_region = r.id
            LEFT JOIN stripe_accounts sa ON e.id = sa.fk_entite
            WHERE e.id = ? AND e.chk_active = 1'
                    );
                    $amicaleStmt->execute([$user['fk_entite']]);
                    $amicales = $amicaleStmt->fetchAll(PDO::FETCH_ASSOC);
                } else {
                    // Admin avec fk_role>2: toutes les amicales sauf id=1
                    $amicaleStmt = $this->db->prepare(
                        'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
            e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
            e.encrypted_email as email, e.gps_lat, e.gps_lng,
            sa.stripe_account_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
            e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass,
            sa.stripe_location_id
            FROM entites e
            LEFT JOIN x_regions r ON e.fk_region = r.id
            LEFT JOIN stripe_accounts sa ON e.id = sa.fk_entite
            WHERE e.id != 1 AND e.chk_active = 1'
                    );
                    $amicaleStmt->execute();
                    $amicales = $amicaleStmt->fetchAll(PDO::FETCH_ASSOC);
                }

                if (!empty($amicales)) {
                    foreach ($amicales as &$amicale) {
                        // Déchiffrement du nom
                        if (!empty($amicale['name'])) {
                            $amicale['name'] = ApiService::decryptData($amicale['name']);
                        }

                        // Déchiffrement de l'email si disponible
                        if (!empty($amicale['email'])) {
                            $decryptedEmail = ApiService::decryptSearchableData($amicale['email']);
                            if ($decryptedEmail) {
                                $amicale['email'] = $decryptedEmail;
                            }
                        }

                        // Déchiffrement du téléphone
                        if (!empty($amicale['phone'])) {
                            $amicale['phone'] = ApiService::decryptData($amicale['phone']);
                        }

                        // Déchiffrement du mobile
                        if (!empty($amicale['mobile'])) {
                            $amicale['mobile'] = ApiService::decryptData($amicale['mobile']);
                        }

                        // Déchiffrement du stripe_id
                        if (!empty($amicale['stripe_id'])) {
                            $amicale['stripe_id'] = ApiService::decryptData($amicale['stripe_id']);
                        }
                    }
                    $amicalesData = $amicales;
                }
            }

            // 8. Récupérer les entités de type 1 pour les utilisateurs avec fk_role > 2
            $entitesData = [];

            if ($user['fk_role'] > 2) {
                // Admin avec fk_role > 2: toutes les entités de type 1
                $entitesStmt = $this->db->prepare(
                    'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville, 
                    e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile, 
                    e.encrypted_email as email, e.gps_lat, e.gps_lng, 
                    e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel, 
                    e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass
                    FROM entites e
                    LEFT JOIN x_regions r ON e.fk_region = r.id
                    WHERE e.fk_type = 1 AND e.chk_active = 1'
                );
                $entitesStmt->execute();
                $entites = $entitesStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($entites)) {
                    foreach ($entites as &$entite) {
                        // Déchiffrement du nom
                        if (!empty($entite['name'])) {
                            $entite['name'] = ApiService::decryptData($entite['name']);
                        }

                        // Déchiffrement de l'email si disponible
                        if (!empty($entite['email'])) {
                            $decryptedEmail = ApiService::decryptSearchableData($entite['email']);
                            if ($decryptedEmail) {
                                $entite['email'] = $decryptedEmail;
                            }
                        }

                        // Déchiffrement du téléphone
                        if (!empty($entite['phone'])) {
                            $entite['phone'] = ApiService::decryptData($entite['phone']);
                        }

                        // Déchiffrement du mobile
                        if (!empty($entite['mobile'])) {
                            $entite['mobile'] = ApiService::decryptData($entite['mobile']);
                        }

                        // Déchiffrement du stripe_id
                        if (!empty($entite['stripe_id'])) {
                            $entite['stripe_id'] = ApiService::decryptData($entite['stripe_id']);
                        }
                    }
                    $entitesData = $entites;
                }
            }

            // Préparation de la réponse
            $response = [
                'status' => 'success',
                'message' => 'Connexion réussie',
                'session_id' => session_id(),
                'session_expiry' => date('c', strtotime('+24 hours')), // Ajoute une expiration de 24h
                'user' => $userData
            ];

            // Ajout des amicales à la racine de la réponse si disponibles
            if (!empty($amicalesData)) {
                // Récupérer le logo de l'entité de l'utilisateur si elle existe
                $logoData = null;
                if (!empty($user['fk_entite'])) {
                    $logoStmt = $this->db->prepare('
                        SELECT id, fichier, file_path, file_type, mime_type, processed_width, processed_height
                        FROM medias 
                        WHERE support = ? AND support_id = ? AND file_category = ?
                        ORDER BY created_at DESC
                        LIMIT 1
                    ');
                    $logoStmt->execute(['entite', $user['fk_entite'], 'logo']);
                    $logo = $logoStmt->fetch(PDO::FETCH_ASSOC);
                    
                    if ($logo && file_exists($logo['file_path'])) {
                        // Lire le fichier et l'encoder en base64
                        $imageData = file_get_contents($logo['file_path']);
                        if ($imageData !== false) {
                            $base64 = base64_encode($imageData);
                            // Format data URL pour usage direct dans Flutter
                            $dataUrl = 'data:' . $logo['mime_type'] . ';base64,' . $base64;
                            
                            $logoData = [
                                'id' => $logo['id'],
                                'data_url' => $dataUrl,  // Image encodée en base64
                                'file_name' => $logo['fichier'],
                                'mime_type' => $logo['mime_type'],
                                'width' => $logo['processed_width'],
                                'height' => $logo['processed_height']
                            ];
                        }
                    }
                }
                
                // Si c'est un tableau avec un seul élément, on envoie directement l'objet
                // pour que le client reçoive un objet et non un tableau avec un seul objet
                if (count($amicalesData) === 1) {
                    $response['amicale'] = $amicalesData[0];
                    // Ajouter le logo à l'amicale si disponible
                    if ($logoData !== null) {
                        $response['amicale']['logo'] = $logoData;
                    }
                } else {
                    $response['amicale'] = $amicalesData;
                    // Pour plusieurs amicales, ajouter le logo à celle de l'utilisateur
                    if ($logoData !== null && !empty($user['fk_entite'])) {
                        foreach ($response['amicale'] as &$amicale) {
                            if ($amicale['id'] == $user['fk_entite']) {
                                $amicale['logo'] = $logoData;
                                break;
                            }
                        }
                    }
                }
            }

            // Ajout des entités à la racine de la réponse sous le nom "clients" (vide pour fk_role <= 2)
            $response['clients'] = $entitesData;

            // Ajout des membres à la racine de la réponse si disponibles
            if (!empty($membresData)) {
                $response['membres'] = $membresData;
            }

            // Ajout des opérations à la racine de la réponse si disponibles
            if (!empty($operationsData)) {
                $response['operations'] = $operationsData;
            }

            // Ajout des secteurs à la racine de la réponse si disponibles
            if (!empty($sectorsData)) {
                $response['sectors'] = $sectorsData;
            }

            // Ajout des passages à la racine de la réponse si disponibles
            if (!empty($passagesData)) {
                $response['passages'] = $passagesData;
            }

            // Ajout des utilisateurs des secteurs à la racine de la réponse si disponibles
            if (!empty($usersSectorsData)) {
                $response['users_sectors'] = $usersSectorsData;
            }

            // 5. Section clients gérée plus bas pour les super-administrateurs

            // 9. Récupérer les régions selon le rôle de l'utilisateur
            $regionsData = [];

            if ($user['fk_role'] <= 2 && !empty($user['fk_entite'])) {
                // User normal ou admin avec fk_role=2: uniquement sa région basée sur le code postal de son amicale
                $amicaleStmt = $this->db->prepare('SELECT code_postal FROM entites WHERE id = ?');
                $amicaleStmt->execute([$user['fk_entite']]);
                $amicale = $amicaleStmt->fetch(PDO::FETCH_ASSOC);

                if (!empty($amicale) && !empty($amicale['code_postal'])) {
                    $departement = substr($amicale['code_postal'], 0, 2);

                    $regionStmt = $this->db->prepare(
                        'SELECT id, fk_pays, libelle, libelle_long, table_osm, departements, chk_active 
                        FROM x_regions 
                        WHERE FIND_IN_SET(?, departements) > 0 AND chk_active = 1'
                    );
                    $regionStmt->execute([$departement]);
                    $regions = $regionStmt->fetchAll(PDO::FETCH_ASSOC);

                    if (!empty($regions)) {
                        $regionsData = $regions;
                    }
                }
            } else {
                // Admin avec fk_role>2: toutes les régions
                $regionStmt = $this->db->prepare(
                    'SELECT id, fk_pays, libelle, libelle_long, table_osm, departements, chk_active 
                    FROM x_regions 
                    WHERE chk_active = 1'
                );
                $regionStmt->execute();
                $regions = $regionStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($regions)) {
                    $regionsData = $regions;
                }
            }

            // Ajout des régions à la racine de la réponse si disponibles
            if (!empty($regionsData)) {
                $response['regions'] = $regionsData;
            }

            // Ajout des informations du module chat
            $chatData = [];
            
            // Récupérer le nombre total de conversations de l'utilisateur
            $roomCountStmt = $this->db->prepare('
                SELECT COUNT(DISTINCT r.id) as total_rooms
                FROM chat_rooms r
                INNER JOIN chat_participants p ON r.id = p.room_id
                WHERE p.user_id = :user_id
                  AND p.left_at IS NULL
                  AND r.is_active = 1
            ');
            $roomCountStmt->execute(['user_id' => $user['id']]);
            $roomCount = $roomCountStmt->fetch(PDO::FETCH_ASSOC);
            $chatData['total_rooms'] = (int)($roomCount['total_rooms'] ?? 0);
            
            // Récupérer le nombre de messages non lus
            $unreadStmt = $this->db->prepare('
                SELECT COUNT(*) as unread_count
                FROM chat_messages m
                INNER JOIN chat_participants p ON m.room_id = p.room_id
                WHERE p.user_id = :user_id
                  AND p.left_at IS NULL
                  AND m.sender_id != :sender_id
                  AND m.sent_at > COALESCE(p.last_read_at, p.joined_at)
                  AND m.is_deleted = 0
            ');
            $unreadStmt->execute([
                'user_id' => $user['id'],
                'sender_id' => $user['id']
            ]);
            $unreadResult = $unreadStmt->fetch(PDO::FETCH_ASSOC);
            $chatData['unread_messages'] = (int)($unreadResult['unread_count'] ?? 0);
            
            // Récupérer la dernière conversation active (optionnel, pour affichage rapide)
            $lastRoomStmt = $this->db->prepare('
                SELECT 
                    r.id,
                    r.title,
                    r.type,
                    (SELECT m.content 
                     FROM chat_messages m 
                     WHERE m.room_id = r.id 
                       AND m.is_deleted = 0 
                     ORDER BY m.sent_at DESC 
                     LIMIT 1) as last_message,
                    (SELECT m.sent_at 
                     FROM chat_messages m 
                     WHERE m.room_id = r.id 
                       AND m.is_deleted = 0 
                     ORDER BY m.sent_at DESC 
                     LIMIT 1) as last_message_at
                FROM chat_rooms r
                INNER JOIN chat_participants p ON r.id = p.room_id
                WHERE p.user_id = :user_id
                  AND p.left_at IS NULL
                  AND r.is_active = 1
                ORDER BY COALESCE(
                    (SELECT MAX(m.sent_at) FROM chat_messages m WHERE m.room_id = r.id),
                    r.created_at
                ) DESC
                LIMIT 1
            ');
            $lastRoomStmt->execute(['user_id' => $user['id']]);
            $lastRoom = $lastRoomStmt->fetch(PDO::FETCH_ASSOC);
            
            if ($lastRoom) {
                $chatData['last_active_room'] = [
                    'id' => $lastRoom['id'],
                    'title' => $lastRoom['title'],
                    'type' => $lastRoom['type'],
                    'last_message' => $lastRoom['last_message'],
                    'last_message_at' => $lastRoom['last_message_at']
                ];
            }
            
            // Indicateur si le chat est disponible pour cet utilisateur
            $chatData['chat_enabled'] = true; // Peut être conditionné selon le rôle ou l'entité
            
            // Ajouter les données du chat à la réponse
            $response['chat'] = $chatData;

            // Log de connexion réussie
            EventLogService::logLoginSuccess($user['id'], $user['fk_entite'] ?? null, $username);

            // Envoi de la réponse
            Response::json($response);
        } catch (PDOException $e) {
            LogService::log('Erreur base de données lors de la connexion GeoSector', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'code' => $e->getCode()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        } catch (Exception $e) {
            LogService::log('Erreur inattendue lors de la connexion GeoSector', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Une erreur inattendue est survenue'
            ], 500);
        }
    }

    public function refreshSession(): void {
        try {
            // 1. Récupérer l'ID utilisateur depuis la session active
            $userId = Session::getUserId();

            if (!$userId) {
                Response::json(['error' => 'Session invalide'], 401);
                return;
            }

            // 2. Récupérer le mode depuis l'URL
            $mode = $_GET['mode'] ?? 'user';

            // 3. Validation du mode
            if (!in_array($mode, ['user', 'admin'])) {
                Response::json(['error' => 'Mode invalide. Valeurs acceptées: user, admin'], 400);
                return;
            }

            // Déterminer le roleCondition selon le mode (même logique que login)
            $roleCondition = ($mode === 'user') ? 'AND fk_role IN (1, 2)' : 'AND fk_role>1';

            // 4. Requête pour récupérer l'utilisateur et son entité (même requête que login)
            $stmt = $this->db->prepare(
                'SELECT
                    u.id, u.encrypted_email, u.encrypted_user_name, u.encrypted_name, u.user_pass_hash,
                    u.first_name, u.fk_role, u.fk_entite, u.fk_titre, u.chk_active, u.sect_name,
                    u.date_naissance, u.date_embauche, u.encrypted_phone, u.encrypted_mobile,
                    e.id AS entite_id, e.encrypted_name AS entite_encrypted_name,
                    e.adresse1, e.code_postal, e.ville, e.gps_lat, e.gps_lng, e.chk_active AS entite_chk_active
                FROM users u
                LEFT JOIN entites e ON u.fk_entite = e.id
                WHERE u.id = ? AND u.chk_active != 0 ' . $roleCondition
            );
            $stmt->execute([$userId]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$user) {
                LogService::log('Rafraîchissement session échoué : utilisateur non trouvé ou accès interdit', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'mode' => $mode
                ]);
                Response::json(['error' => 'Utilisateur non trouvé ou accès interdit à cette interface'], 403);
                return;
            }

            // Vérifier si l'utilisateur a une entité et si elle est active
            if (!empty($user['fk_entite']) && (!isset($user['entite_chk_active']) || $user['entite_chk_active'] != 1)) {
                LogService::log('Rafraîchissement session échoué : entité non active', [
                    'level' => 'warning',
                    'userId' => $userId,
                    'entite_id' => $user['fk_entite']
                ]);
                Response::json([
                    'status' => 'error',
                    'message' => 'Votre amicale n\'est pas activée. Veuillez contacter votre administrateur.'
                ], 403);
                return;
            }

            // Déchiffrement du nom
            $decryptedName = ApiService::decryptData($user['encrypted_name']);
            $username = ApiService::decryptSearchableData($user['encrypted_user_name']);

            // Déchiffrement de l'email si disponible
            $email = '';
            if (!empty($user['encrypted_email'])) {
                $email = ApiService::decryptSearchableData($user['encrypted_email']);

                if (empty($email)) {
                    LogService::log('Déchiffrement email échoué', [
                        'level' => 'error',
                        'message' => 'Déchiffrement de l\'email échoué',
                        'encrypted_email' => $user['encrypted_email'],
                        'user_id' => $user['id']
                    ]);

                    Response::json([
                        'status' => 'error',
                        'message' => 'Erreur de déchiffrement de l\'email. Exécutez le script de migration pour résoudre ce problème.',
                        'debug_info' => [
                            'encrypted_email' => $user['encrypted_email'],
                            'user_id' => $user['id']
                        ]
                    ], 500);
                    return;
                }
            }

            // Préparation des données utilisateur pour la réponse
            $userData = [
                'id' => $user['id'],
                'fk_entite' => $user['fk_entite'] ?? null,
                'fk_role' => $user['fk_role'] ?? '0',
                'fk_titre' => $user['fk_titre'] ?? null,
                'first_name' => $user['first_name'] ?? '',
                'sect_name' => $user['sect_name'] ?? '',
                'date_naissance' => $user['date_naissance'] ?? null,
                'date_embauche' => $user['date_embauche'] ?? null,
                'username' => $username,
                'name' => $decryptedName
            ];

            // Déchiffrement du téléphone
            if (!empty($user['encrypted_phone'])) {
                $userData['phone'] = ApiService::decryptData($user['encrypted_phone']);
            } else {
                $userData['phone'] = '';
            }

            // Déchiffrement du mobile
            if (!empty($user['encrypted_mobile'])) {
                $userData['mobile'] = ApiService::decryptData($user['encrypted_mobile']);
            } else {
                $userData['mobile'] = '';
            }

            $userData['email'] = $email;

            // 5. Charger toutes les données selon le mode (MÊME LOGIQUE QUE LOGIN)
            $operationsData = [];
            $sectorsData = [];
            $passagesData = [];
            $usersSectorsData = [];

            // Récupération des opérations selon les critères
            $operationLimit = 0;
            $activeOperationOnly = false;

            if ($mode === 'user') {
                $operationLimit = 1;
                $activeOperationOnly = true;
            } elseif ($mode === 'admin' && $user['fk_role'] == 2) {
                $operationLimit = 3;
            } elseif ($mode === 'admin' && $user['fk_role'] > 2) {
                $operationLimit = 10;
            } else {
                $operationLimit = 0;
            }

            if ($operationLimit > 0 && !empty($user['fk_entite'])) {
                $operationQuery = "SELECT id, fk_entite, libelle, date_deb, date_fin, chk_active
                                   FROM operations
                                   WHERE fk_entite = ?";

                if ($activeOperationOnly) {
                    $operationQuery .= " AND chk_active = 1";
                }

                $operationQuery .= " ORDER BY id DESC LIMIT " . $operationLimit;

                $operationStmt = $this->db->prepare($operationQuery);
                $operationStmt->execute([$user['fk_entite']]);
                $operations = $operationStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($operations)) {
                    foreach ($operations as $operation) {
                        $operationsData[] = [
                            'id' => $operation['id'],
                            'fk_entite' => $operation['fk_entite'],
                            'libelle' => $operation['libelle'],
                            'date_deb' => $operation['date_deb'],
                            'date_fin' => $operation['date_fin'],
                            'chk_active' => $operation['chk_active']
                        ];
                    }

                    $activeOperationId = $operations[0]['id'];

                    // Récupérer ope_user_id pour l'utilisateur connecté et l'opération active
                    $opeUserStmt = $this->db->prepare(
                        'SELECT id FROM ope_users WHERE fk_user = ? AND fk_operation = ?'
                    );
                    $opeUserStmt->execute([$user['id'], $activeOperationId]);
                    $opeUser = $opeUserStmt->fetch(PDO::FETCH_ASSOC);

                    if ($opeUser) {
                        $userData['ope_user_id'] = $opeUser['id'];
                    } else {
                        $userData['ope_user_id'] = null;
                    }

                    // Récupérer les secteurs selon le mode et le rôle
                    if ($mode === 'user') {
                        // Utiliser ope_user_id au lieu de users.id
                        $opeUserId = $userData['ope_user_id'];
                        if ($opeUserId) {
                            $sectorsStmt = $this->db->prepare(
                                'SELECT s.id, s.libelle, s.color, s.sector
                                FROM ope_sectors s
                                JOIN ope_users_sectors us ON s.id = us.fk_sector
                                WHERE us.fk_operation = ? AND us.fk_user = ? AND us.chk_active = 1 AND s.chk_active = 1'
                            );
                            $sectorsStmt->execute([$activeOperationId, $opeUserId]);
                        }
                    } elseif ($mode === 'admin' && $user['fk_role'] == 2) {
                        $sectorsStmt = $this->db->prepare(
                            'SELECT DISTINCT s.id, s.libelle, s.color, s.sector
                            FROM ope_sectors s
                            WHERE s.fk_operation = ? AND s.chk_active = 1'
                        );
                        $sectorsStmt->execute([$activeOperationId]);
                    } else {
                        $sectors = [];
                        $sectorsData = [];
                    }

                    if (isset($sectorsStmt)) {
                        $sectors = $sectorsStmt->fetchAll(PDO::FETCH_ASSOC);
                    } else {
                        $sectors = [];
                    }

                    if (!empty($sectors)) {
                        $sectorsData = $sectors;

                        // Récupérer les passages selon le mode et le rôle
                        if ($mode === 'user' && !empty($sectors)) {
                            // Utiliser ope_user_id au lieu de users.id
                            $opeUserId = $userData['ope_user_id'];
                            $sectorIds = array_column($sectors, 'id');
                            $sectorIdsString = implode(',', $sectorIds);

                            if (!empty($sectorIdsString) && $opeUserId) {
                                $passagesStmt = $this->db->prepare(
                                    "SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau,
                                    gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages
                                    FROM ope_pass
                                    WHERE fk_operation = ?
                                    AND chk_active = 1
                                    AND (
                                        (fk_user = ?)
                                        OR
                                        (fk_sector IN ($sectorIdsString) AND fk_type = 2 AND fk_user != ?)
                                    )
                                    ORDER BY passed_at DESC"
                                );
                                $passagesStmt->execute([$activeOperationId, $opeUserId, $opeUserId]);
                            }
                        } elseif ($mode === 'admin' && $user['fk_role'] == 2) {
                            $passagesStmt = $this->db->prepare(
                                "SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau,
                                gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages
                                FROM ope_pass
                                WHERE fk_operation = ? AND chk_active = 1"
                            );
                            $passagesStmt->execute([$activeOperationId]);
                        } else {
                            $passages = [];
                            $passagesData = [];
                        }

                        if (isset($passagesStmt)) {
                            $passages = $passagesStmt->fetchAll(PDO::FETCH_ASSOC);
                        } else {
                            $passages = [];
                        }

                        if (!empty($passages)) {
                            foreach ($passages as &$passage) {
                                $passage['name'] = '';
                                if (!empty($passage['encrypted_name'])) {
                                    $passage['name'] = ApiService::decryptData($passage['encrypted_name']);
                                }
                                unset($passage['encrypted_name']);

                                $passage['email'] = '';
                                if (!empty($passage['encrypted_email'])) {
                                    $decryptedEmail = ApiService::decryptSearchableData($passage['encrypted_email']);
                                    if ($decryptedEmail) {
                                        $passage['email'] = $decryptedEmail;
                                    }
                                }
                                unset($passage['encrypted_email']);

                                $passage['phone'] = '';
                                if (!empty($passage['encrypted_phone'])) {
                                    $passage['phone'] = ApiService::decryptData($passage['encrypted_phone']);
                                }
                                unset($passage['encrypted_phone']);
                            }
                            $passagesData = $passages;
                        }

                        // Récupérer les utilisateurs des secteurs partagés
                        if (($mode === 'user' || ($mode === 'admin' && $user['fk_role'] == 2)) && !empty($sectors)) {
                            $sectorIds = array_column($sectors, 'id');
                            $sectorIdsString = implode(',', $sectorIds);

                            if (!empty($sectorIdsString)) {
                                $usersSectorsStmt = $this->db->prepare(
                                    "SELECT DISTINCT u.id as user_id, ou.id as ope_user_id, ou.first_name, u.encrypted_name, u.sect_name, us.fk_sector
                                    FROM users u
                                    JOIN ope_users ou ON u.id = ou.fk_user
                                    JOIN ope_users_sectors us ON ou.id = us.fk_user AND ou.fk_operation = us.fk_operation
                                    WHERE us.fk_sector IN ($sectorIdsString)
                                    AND us.fk_operation = ?
                                    AND us.chk_active = 1
                                    AND u.chk_active = 1
                                    AND u.id != ?"
                                );
                                $usersSectorsStmt->execute([$activeOperationId, $user['id']]);
                                $usersSectors = $usersSectorsStmt->fetchAll(PDO::FETCH_ASSOC);

                                if (!empty($usersSectors)) {
                                    foreach ($usersSectors as &$userSector) {
                                        if (!empty($userSector['encrypted_name'])) {
                                            $userSector['name'] = ApiService::decryptData($userSector['encrypted_name']);
                                            unset($userSector['encrypted_name']);
                                        }
                                    }
                                    $usersSectorsData = $usersSectors;
                                }
                            }
                        } else {
                            $usersSectorsData = [];
                        }
                    }
                }
            }

            // Récupérer les membres si nécessaire
            $membresData = [];
            if ($mode === 'admin' && $user['fk_role'] == 2 && !empty($user['fk_entite'])) {
                // Si on a une opération active, on récupère aussi ope_user_id
                if (isset($activeOperationId)) {
                    $membresStmt = $this->db->prepare(
                        'SELECT u.id, u.fk_role, u.fk_entite, u.fk_titre, u.encrypted_name, u.first_name, u.sect_name,
                         u.encrypted_user_name, u.encrypted_phone, u.encrypted_mobile, u.encrypted_email,
                         u.date_naissance, u.date_embauche, u.chk_active, ou.id as ope_user_id
                         FROM users u
                         LEFT JOIN ope_users ou ON u.id = ou.fk_user AND ou.fk_operation = ?
                         WHERE u.fk_entite = ?'
                    );
                    $membresStmt->execute([$activeOperationId, $user['fk_entite']]);
                } else {
                    $membresStmt = $this->db->prepare(
                        'SELECT id, fk_role, fk_entite, fk_titre, encrypted_name, first_name, sect_name,
                         encrypted_user_name, encrypted_phone, encrypted_mobile, encrypted_email,
                         date_naissance, date_embauche, chk_active
                         FROM users
                         WHERE fk_entite = ?'
                    );
                    $membresStmt->execute([$user['fk_entite']]);
                }
                $membres = $membresStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($membres)) {
                    foreach ($membres as $membre) {
                        $membreItem = [
                            'id' => $membre['id'],
                            'ope_user_id' => $membre['ope_user_id'] ?? null,
                            'fk_role' => $membre['fk_role'],
                            'fk_entite' => $membre['fk_entite'],
                            'fk_titre' => $membre['fk_titre'],
                            'first_name' => $membre['first_name'] ?? '',
                            'sect_name' => $membre['sect_name'] ?? '',
                            'date_naissance' => $membre['date_naissance'] ?? null,
                            'date_embauche' => $membre['date_embauche'] ?? null,
                            'chk_active' => $membre['chk_active']
                        ];

                        if (!empty($membre['encrypted_name'])) {
                            $membreItem['name'] = ApiService::decryptData($membre['encrypted_name']);
                        } else {
                            $membreItem['name'] = '';
                        }

                        if (!empty($membre['encrypted_user_name'])) {
                            $membreItem['username'] = ApiService::decryptSearchableData($membre['encrypted_user_name']);
                        } else {
                            $membreItem['username'] = '';
                        }

                        if (!empty($membre['encrypted_phone'])) {
                            $membreItem['phone'] = ApiService::decryptData($membre['encrypted_phone']);
                        } else {
                            $membreItem['phone'] = '';
                        }

                        if (!empty($membre['encrypted_mobile'])) {
                            $membreItem['mobile'] = ApiService::decryptData($membre['encrypted_mobile']);
                        } else {
                            $membreItem['mobile'] = '';
                        }

                        if (!empty($membre['encrypted_email'])) {
                            $decryptedEmail = ApiService::decryptSearchableData($membre['encrypted_email']);
                            if ($decryptedEmail) {
                                $membreItem['email'] = $decryptedEmail;
                            }
                        } else {
                            $membreItem['email'] = '';
                        }

                        $membresData[] = $membreItem;
                    }
                }
            }

            // Récupérer les amicales selon le rôle
            $amicalesData = [];
            if (!empty($user['fk_entite'])) {
                if ($user['fk_role'] <= 2) {
                    $amicaleStmt = $this->db->prepare(
                        'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
                        e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
                        e.encrypted_email as email, e.gps_lat, e.gps_lng,
                        sa.stripe_account_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
                        e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass,
                        sa.stripe_location_id
                        FROM entites e
                        LEFT JOIN x_regions r ON e.fk_region = r.id
                        LEFT JOIN stripe_accounts sa ON e.id = sa.fk_entite
                        WHERE e.id = ? AND e.chk_active = 1'
                    );
                    $amicaleStmt->execute([$user['fk_entite']]);
                    $amicales = $amicaleStmt->fetchAll(PDO::FETCH_ASSOC);
                } else {
                    $amicaleStmt = $this->db->prepare(
                        'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
                        e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
                        e.encrypted_email as email, e.gps_lat, e.gps_lng,
                        sa.stripe_account_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
                        e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass,
                        sa.stripe_location_id
                        FROM entites e
                        LEFT JOIN x_regions r ON e.fk_region = r.id
                        LEFT JOIN stripe_accounts sa ON e.id = sa.fk_entite
                        WHERE e.id != 1 AND e.chk_active = 1'
                    );
                    $amicaleStmt->execute();
                    $amicales = $amicaleStmt->fetchAll(PDO::FETCH_ASSOC);
                }

                if (!empty($amicales)) {
                    foreach ($amicales as &$amicale) {
                        if (!empty($amicale['name'])) {
                            $amicale['name'] = ApiService::decryptData($amicale['name']);
                        }
                        if (!empty($amicale['email'])) {
                            $decryptedEmail = ApiService::decryptSearchableData($amicale['email']);
                            if ($decryptedEmail) {
                                $amicale['email'] = $decryptedEmail;
                            }
                        }
                        if (!empty($amicale['phone'])) {
                            $amicale['phone'] = ApiService::decryptData($amicale['phone']);
                        }
                        if (!empty($amicale['mobile'])) {
                            $amicale['mobile'] = ApiService::decryptData($amicale['mobile']);
                        }
                        if (!empty($amicale['stripe_id'])) {
                            $amicale['stripe_id'] = ApiService::decryptData($amicale['stripe_id']);
                        }
                    }
                    $amicalesData = $amicales;
                }
            }

            // Récupérer les entités de type 1 pour les utilisateurs avec fk_role > 2
            $entitesData = [];
            if ($user['fk_role'] > 2) {
                $entitesStmt = $this->db->prepare(
                    'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
                    e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
                    e.encrypted_email as email, e.gps_lat, e.gps_lng,
                    e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_mdp_manuel, e.chk_username_manuel,
                    e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe, e.chk_user_delete_pass
                    FROM entites e
                    LEFT JOIN x_regions r ON e.fk_region = r.id
                    WHERE e.fk_type = 1 AND e.chk_active = 1'
                );
                $entitesStmt->execute();
                $entites = $entitesStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($entites)) {
                    foreach ($entites as &$entite) {
                        if (!empty($entite['name'])) {
                            $entite['name'] = ApiService::decryptData($entite['name']);
                        }
                        if (!empty($entite['email'])) {
                            $decryptedEmail = ApiService::decryptSearchableData($entite['email']);
                            if ($decryptedEmail) {
                                $entite['email'] = $decryptedEmail;
                            }
                        }
                        if (!empty($entite['phone'])) {
                            $entite['phone'] = ApiService::decryptData($entite['phone']);
                        }
                        if (!empty($entite['mobile'])) {
                            $entite['mobile'] = ApiService::decryptData($entite['mobile']);
                        }
                        if (!empty($entite['stripe_id'])) {
                            $entite['stripe_id'] = ApiService::decryptData($entite['stripe_id']);
                        }
                    }
                    $entitesData = $entites;
                }
            }

            // Préparation de la réponse (MÊME STRUCTURE QUE LOGIN)
            $response = [
                'status' => 'success',
                'message' => 'Session rafraîchie',
                'session_id' => session_id(),
                'session_expiry' => date('c', strtotime('+24 hours')),
                'user' => $userData
            ];

            // Ajout des amicales avec logo
            if (!empty($amicalesData)) {
                $logoData = null;
                if (!empty($user['fk_entite'])) {
                    $logoStmt = $this->db->prepare('
                        SELECT id, fichier, file_path, file_type, mime_type, processed_width, processed_height
                        FROM medias
                        WHERE support = ? AND support_id = ? AND file_category = ?
                        ORDER BY created_at DESC
                        LIMIT 1
                    ');
                    $logoStmt->execute(['entite', $user['fk_entite'], 'logo']);
                    $logo = $logoStmt->fetch(PDO::FETCH_ASSOC);

                    if ($logo && file_exists($logo['file_path'])) {
                        $imageData = file_get_contents($logo['file_path']);
                        if ($imageData !== false) {
                            $base64 = base64_encode($imageData);
                            $dataUrl = 'data:' . $logo['mime_type'] . ';base64,' . $base64;

                            $logoData = [
                                'id' => $logo['id'],
                                'data_url' => $dataUrl,
                                'file_name' => $logo['fichier'],
                                'mime_type' => $logo['mime_type'],
                                'width' => $logo['processed_width'],
                                'height' => $logo['processed_height']
                            ];
                        }
                    }
                }

                if (count($amicalesData) === 1) {
                    $response['amicale'] = $amicalesData[0];
                    if ($logoData !== null) {
                        $response['amicale']['logo'] = $logoData;
                    }
                } else {
                    $response['amicale'] = $amicalesData;
                    if ($logoData !== null && !empty($user['fk_entite'])) {
                        foreach ($response['amicale'] as &$amicale) {
                            if ($amicale['id'] == $user['fk_entite']) {
                                $amicale['logo'] = $logoData;
                                break;
                            }
                        }
                    }
                }
            }

            $response['clients'] = $entitesData;

            if (!empty($membresData)) {
                $response['membres'] = $membresData;
            }

            if (!empty($operationsData)) {
                $response['operations'] = $operationsData;
            }

            if (!empty($sectorsData)) {
                $response['sectors'] = $sectorsData;
            }

            if (!empty($passagesData)) {
                $response['passages'] = $passagesData;
            }

            if (!empty($usersSectorsData)) {
                $response['users_sectors'] = $usersSectorsData;
            }

            // Récupérer les régions selon le rôle
            $regionsData = [];
            if ($user['fk_role'] <= 2 && !empty($user['fk_entite'])) {
                $amicaleStmt = $this->db->prepare('SELECT code_postal FROM entites WHERE id = ?');
                $amicaleStmt->execute([$user['fk_entite']]);
                $amicale = $amicaleStmt->fetch(PDO::FETCH_ASSOC);

                if (!empty($amicale) && !empty($amicale['code_postal'])) {
                    $departement = substr($amicale['code_postal'], 0, 2);

                    $regionStmt = $this->db->prepare(
                        'SELECT id, fk_pays, libelle, libelle_long, table_osm, departements, chk_active
                        FROM x_regions
                        WHERE FIND_IN_SET(?, departements) > 0 AND chk_active = 1'
                    );
                    $regionStmt->execute([$departement]);
                    $regions = $regionStmt->fetchAll(PDO::FETCH_ASSOC);

                    if (!empty($regions)) {
                        $regionsData = $regions;
                    }
                }
            } else {
                $regionStmt = $this->db->prepare(
                    'SELECT id, fk_pays, libelle, libelle_long, table_osm, departements, chk_active
                    FROM x_regions
                    WHERE chk_active = 1'
                );
                $regionStmt->execute();
                $regions = $regionStmt->fetchAll(PDO::FETCH_ASSOC);

                if (!empty($regions)) {
                    $regionsData = $regions;
                }
            }

            if (!empty($regionsData)) {
                $response['regions'] = $regionsData;
            }

            // Ajout des informations du module chat
            $chatData = [];

            $roomCountStmt = $this->db->prepare('
                SELECT COUNT(DISTINCT r.id) as total_rooms
                FROM chat_rooms r
                INNER JOIN chat_participants p ON r.id = p.room_id
                WHERE p.user_id = :user_id
                  AND p.left_at IS NULL
                  AND r.is_active = 1
            ');
            $roomCountStmt->execute(['user_id' => $user['id']]);
            $roomCount = $roomCountStmt->fetch(PDO::FETCH_ASSOC);
            $chatData['total_rooms'] = (int)($roomCount['total_rooms'] ?? 0);

            $unreadStmt = $this->db->prepare('
                SELECT COUNT(*) as unread_count
                FROM chat_messages m
                INNER JOIN chat_participants p ON m.room_id = p.room_id
                WHERE p.user_id = :user_id
                  AND p.left_at IS NULL
                  AND m.sender_id != :sender_id
                  AND m.sent_at > COALESCE(p.last_read_at, p.joined_at)
                  AND m.is_deleted = 0
            ');
            $unreadStmt->execute([
                'user_id' => $user['id'],
                'sender_id' => $user['id']
            ]);
            $unreadResult = $unreadStmt->fetch(PDO::FETCH_ASSOC);
            $chatData['unread_messages'] = (int)($unreadResult['unread_count'] ?? 0);

            $lastRoomStmt = $this->db->prepare('
                SELECT
                    r.id,
                    r.title,
                    r.type,
                    (SELECT m.content
                     FROM chat_messages m
                     WHERE m.room_id = r.id
                       AND m.is_deleted = 0
                     ORDER BY m.sent_at DESC
                     LIMIT 1) as last_message,
                    (SELECT m.sent_at
                     FROM chat_messages m
                     WHERE m.room_id = r.id
                       AND m.is_deleted = 0
                     ORDER BY m.sent_at DESC
                     LIMIT 1) as last_message_at
                FROM chat_rooms r
                INNER JOIN chat_participants p ON r.id = p.room_id
                WHERE p.user_id = :user_id
                  AND p.left_at IS NULL
                  AND r.is_active = 1
                ORDER BY COALESCE(
                    (SELECT MAX(m.sent_at) FROM chat_messages m WHERE m.room_id = r.id),
                    r.created_at
                ) DESC
                LIMIT 1
            ');
            $lastRoomStmt->execute(['user_id' => $user['id']]);
            $lastRoom = $lastRoomStmt->fetch(PDO::FETCH_ASSOC);

            if ($lastRoom) {
                $chatData['last_active_room'] = [
                    'id' => $lastRoom['id'],
                    'title' => $lastRoom['title'],
                    'type' => $lastRoom['type'],
                    'last_message' => $lastRoom['last_message'],
                    'last_message_at' => $lastRoom['last_message_at']
                ];
            }

            $chatData['chat_enabled'] = true;
            $response['chat'] = $chatData;

            // 6. Envoi de la réponse
            Response::json($response);

        } catch (PDOException $e) {
            LogService::log('Erreur base de données lors du rafraîchissement de session', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'code' => $e->getCode()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur serveur'
            ], 500);
        } catch (Exception $e) {
            LogService::log('Erreur inattendue lors du rafraîchissement de session', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Une erreur inattendue est survenue'
            ], 500);
        }
    }

    public function lostPassword(): void {
        try {
            $data = Request::getJson();

            if (!isset($data['email']) || empty($data['email'])) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Email requis'
                ], 400);
                return;
            }

            $email = trim($data['email']);

            // Validation de l'email
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Format d\'email invalide'
                ], 400);
                return;
            }

            // Chiffrement de l'email pour la recherche
            $encryptedEmail = ApiService::encryptSearchableData($email);

            // Recherche de TOUS les utilisateurs avec cet email (actifs ou non)
            $stmt = $this->db->prepare('
                SELECT id, encrypted_name, encrypted_user_name, chk_active 
                FROM users 
                WHERE encrypted_email = ?
            ');
            $stmt->execute([$encryptedEmail]);
            $users = $stmt->fetchAll(PDO::FETCH_ASSOC);

            if (empty($users)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Aucun compte trouvé avec cet email'
                ], 404);
                return;
            }

            // Génération d'un nouveau mot de passe unique pour tous les comptes
            $newPassword = ApiService::generateSecurePassword();
            $passwordHash = password_hash($newPassword, PASSWORD_DEFAULT);

            // Mise à jour du mot de passe pour TOUS les comptes avec cet email
            $updateStmt = $this->db->prepare('
                UPDATE users 
                SET user_pass_hash = ?, updated_at = NOW() 
                WHERE encrypted_email = ?
            ');
            $updateStmt->execute([$passwordHash, $encryptedEmail]);
            
            // Récupération du nombre de comptes mis à jour
            $updatedCount = $updateStmt->rowCount();

            // Collecte des usernames et du premier nom pour l'email
            $usernames = [];
            $firstName = '';
            foreach ($users as $user) {
                $username = ApiService::decryptSearchableData($user['encrypted_user_name']);
                if ($username) {
                    $usernames[] = $username;
                }
                // Utiliser le premier nom trouvé pour personnaliser l'email
                if (empty($firstName) && !empty($user['encrypted_name'])) {
                    $firstName = ApiService::decryptData($user['encrypted_name']);
                }
            }
            
            // Si aucun nom n'a été trouvé, utiliser "Utilisateur"
            if (empty($firstName)) {
                $firstName = 'Utilisateur';
            }

            // Envoi d'un seul email avec le nouveau mot de passe et la liste des comptes affectés
            $emailData = [
                'username' => implode(', ', $usernames), // Liste tous les usernames concernés
                'password' => $newPassword
            ];
            
            $emailSent = ApiService::sendEmail(
                $email,
                $firstName,
                'lostpwd',
                $emailData
            );

            if ($emailSent) {
                LogService::log('Réinitialisation mot de passe GeoSector réussie', [
                    'level' => 'info',
                    'email' => $email,
                    'comptes_modifies' => $updatedCount,
                    'usernames' => $usernames
                ]);

                $message = $updatedCount > 1 
                    ? sprintf('Un nouveau mot de passe a été envoyé pour les %d comptes associés à votre adresse email', $updatedCount)
                    : 'Un nouveau mot de passe a été envoyé à votre adresse email';

                Response::json([
                    'status' => 'success',
                    'message' => $message
                ]);
            } else {
                LogService::log('Échec envoi email réinitialisation mot de passe GeoSector', [
                    'level' => 'error',
                    'email' => $email,
                    'comptes_modifies' => $updatedCount
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Impossible d\'envoyer l\'email. Veuillez contacter l\'administrateur.'
                ], 500);
            }
        } catch (Exception $e) {
            LogService::log('Erreur lors de la réinitialisation du mot de passe GeoSector', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);

            Response::json([
                'status' => 'error',
                'message' => 'Une erreur est survenue. Veuillez réessayer.'
            ], 500);
        }
    }

    public function register(): void {
        try {
            $data = Request::getJson();

            // 1. Validation des données de base
            if (
                !isset($data['email'], $data['name'], $data['amicale_name'], $data['postal_code'], $data['city_name']) ||
                empty($data['email']) || empty($data['name']) || empty($data['amicale_name']) || empty($data['postal_code'])
            ) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Tous les champs sont requis'
                ], 400);
                return;
            }

            // 2. Validation du token et du captcha
            if (!isset($data['token']) || empty($data['token'])) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Token de sécurité manquant'
                ], 400);
                return;
            }

            // Vérification que le token est un timestamp valide et récent
            // Le frontend envoie un timestamp en millisecondes, donc on le convertit en secondes
            $tokenTimestamp = intval($data['token']) / 1000; // Conversion millisecondes -> secondes
            $currentTime = time();
            $twoHoursAgo = $currentTime - 7200; // 2 heures = 7200 secondes (plus permissif)

            // Tolérance de 5 minutes pour les décalages d'horloge
            $futureTime = $currentTime + 300; // 5 minutes = 300 secondes

            // Log pour le débogage
            LogService::log('Vérification du token', [
                'level' => 'info',
                'token_ms' => $data['token'],
                'token_sec' => $tokenTimestamp,
                'current_time' => $currentTime,
                'two_hours_ago' => $twoHoursAgo,
                'future_time' => $futureTime
            ]);

            // Vérification plus permissive
            if ($tokenTimestamp < $twoHoursAgo || $tokenTimestamp > $futureTime) {
                LogService::log('Tentative d\'inscription avec un token invalide', [
                    'level' => 'warning',
                    'token' => $data['token'],
                    'token_sec' => $tokenTimestamp,
                    'current_time' => $currentTime,
                    'email' => $data['email'] ?? 'non fourni'
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Session expirée, veuillez rafraîchir la page et réessayer'
                ], 400);
                return;
            }

            if (
                !isset($data['captcha_answer'], $data['captcha_expected']) ||
                $data['captcha_answer'] != $data['captcha_expected']
            ) {
                LogService::log('Tentative d\'inscription avec un captcha invalide', [
                    'level' => 'warning',
                    'captcha_answer' => $data['captcha_answer'] ?? 'non fourni',
                    'captcha_expected' => $data['captcha_expected'] ?? 'non fourni',
                    'email' => $data['email'] ?? 'non fourni'
                ]);

                Response::json([
                    'status' => 'error',
                    'message' => 'Vérification anti-robot échouée'
                ], 400);
                return;
            }

            $email = trim($data['email']);
            $name = trim($data['name']);
            $amicaleName = trim($data['amicale_name']);
            $postalCode = trim($data['postal_code']);
            $cityName = trim($data['city_name'] ?? '');

            // 3. Validation de l'email
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Format d\'email invalide'
                ], 400);
                return;
            }

            // 4. Vérification de l'existence de l'email
            // DÉSACTIVÉ : Le client souhaite permettre plusieurs comptes avec le même email
            $encryptedEmail = ApiService::encryptSearchableData($email);
            /*
            $checkStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_email = ?');
            $checkStmt->execute([$encryptedEmail]);
            if ($checkStmt->fetch()) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Cet email est déjà utilisé'
                ], 409);
                return;
            }
            */

            // 5. Vérification de l'existence du code postal + ville dans la table entites
            $checkPostalStmt = $this->db->prepare('SELECT id FROM entites WHERE code_postal = ? AND ville = ?');
            $checkPostalStmt->execute([$postalCode, $cityName]);
            if ($checkPostalStmt->fetch()) {
                Response::json([
                    'status' => 'error',
                    'message' => 'Une amicale est déjà inscrite pour ce code postal et cette ville'
                ], 409);
                return;
            }

            // 6. Recherche de la région correspondant au code postal
            $departement = substr($postalCode, 0, 2);
            $regionStmt = $this->db->prepare('
                SELECT id FROM x_regions 
                WHERE FIND_IN_SET(?, departements) > 0 AND chk_active = 1
                LIMIT 1
            ');
            $regionStmt->execute([$departement]);
            $region = $regionStmt->fetch(PDO::FETCH_ASSOC);
            $regionId = $region ? $region['id'] : null;

            // 7. Chiffrement des données sensibles
            $encryptedName = ApiService::encryptData($name);
            $encryptedAmicaleName = ApiService::encryptData($amicaleName);
            $encryptedEmail = ApiService::encryptSearchableData($email);

            // 8. Création de l'entité (amicale)
            $this->db->beginTransaction();
            try {
                // Insertion de la nouvelle entité
                $entiteStmt = $this->db->prepare('
                    INSERT INTO entites (
                        encrypted_name,
                        code_postal,
                        ville,
                        fk_type,
                        fk_region,
                        encrypted_email,
                        chk_demo,
                        chk_active,
                        created_at
                    ) VALUES (?, ?, ?, 1, ?, ?, 1, 1, NOW())
                ');
                $entiteStmt->execute([
                    $encryptedAmicaleName,
                    $postalCode,
                    $cityName,
                    $regionId,
                    $encryptedEmail
                ]);
                $entiteId = $this->db->lastInsertId();

                if (!$entiteId) {
                    throw new Exception('Erreur lors de la création de l\'entité');
                }

                // Recherche des coordonnées GPS de la caserne de pompiers
                try {
                    $gpsCoordinates = $this->findFireStationCoordinates($postalCode, $cityName);

                    if ($gpsCoordinates) {
                        // Mise à jour des coordonnées GPS de l'entité
                        $updateGpsStmt = $this->db->prepare('
                            UPDATE entites 
                            SET gps_lat = ?, gps_lng = ? 
                            WHERE id = ?
                        ');
                        $updateGpsStmt->execute([
                            $gpsCoordinates['lat'],
                            $gpsCoordinates['lng'],
                            $entiteId
                        ]);

                        LogService::log('Coordonnées GPS de la caserne de pompiers ajoutées', [
                            'level' => 'info',
                            'entiteId' => $entiteId,
                            'postalCode' => $postalCode,
                            'cityName' => $cityName,
                            'lat' => $gpsCoordinates['lat'],
                            'lng' => $gpsCoordinates['lng']
                        ]);
                    } else {
                        LogService::log('Aucune caserne de pompiers trouvée', [
                            'level' => 'warning',
                            'entiteId' => $entiteId,
                            'postalCode' => $postalCode,
                            'cityName' => $cityName
                        ]);
                    }
                } catch (Exception $e) {
                    // On ne bloque pas l'inscription si la recherche de coordonnées échoue
                    LogService::log('Erreur lors de la recherche des coordonnées GPS', [
                        'level' => 'error',
                        'entiteId' => $entiteId,
                        'postalCode' => $postalCode,
                        'cityName' => $cityName,
                        'error' => $e->getMessage()
                    ]);
                }

                // 9. Génération du nom d'utilisateur et du mot de passe
                $username = ApiService::generateUserName($this->db, $name, $postalCode, $cityName);
                $encryptedUsername = ApiService::encryptSearchableData($username);
                $password = ApiService::generateSecurePassword();
                $passwordHash = password_hash($password, PASSWORD_DEFAULT);

                // 10. Création de l'utilisateur administrateur
                $userStmt = $this->db->prepare('
                    INSERT INTO users (
                        encrypted_user_name, 
                        encrypted_email, 
                        user_pass_hash, 
                        encrypted_name, 
                        fk_role, 
                        created_at, 
                        chk_active, 
                        fk_entite
                    ) VALUES (?, ?, ?, ?, 2, NOW(), 1, ?)
                ');
                $userStmt->execute([
                    $encryptedUsername,
                    $encryptedEmail,
                    $passwordHash,
                    $encryptedName,
                    $entiteId
                ]);
                $userId = $this->db->lastInsertId();

                $this->db->commit();

                // Log du succès de l'inscription
                LogService::log('Inscription GeoSector réussie', [
                    'level' => 'info',
                    'userId' => $userId,
                    'username' => $username,
                    'email' => $email,
                    'role' => 2,
                    'entiteId' => $entiteId,
                    'amicaleName' => $amicaleName,
                    'postalCode' => $postalCode,
                    'cityName' => $cityName
                ]);

                // 11. Envoi des emails
                // Premier email : bienvenue avec UNIQUEMENT le nom d'utilisateur (sans mot de passe)
                // Création d'un mot de passe temporaire pour le template (ne sera pas affiché)
                $tempPassword = "********";
                $welcomeResult = ApiService::sendEmail(
                    $email,
                    $name,
                    'welcome',
                    ['username' => $username, 'password' => $tempPassword]
                );

                // Email de notification aux administrateurs (sans le nom d'utilisateur ni le mot de passe)
                $notificationMessage = "Nouvelle inscription GeoSector:\n\n" .
                    "Nom: $name\n" .
                    "Email: $email\n" .
                    "Amicale: $amicaleName\n" .
                    "Code postal: $postalCode\n" .
                    "Ville: $cityName\n";

                ApiService::sendEmail(
                    "contactgeosector@gmail.com",
                    "Admin GeoSector",
                    'alert',
                    ['subject' => 'Nouvelle inscription GeoSector', 'message' => $notificationMessage]
                );

                ApiService::sendEmail(
                    "contact@geosector.fr",
                    "Admin GeoSector",
                    'alert',
                    ['subject' => 'Nouvelle inscription GeoSector', 'message' => $notificationMessage]
                );

                // Attendre un court délai avant d'envoyer le second email (pour éviter les filtres anti-spam)
                sleep(2);

                // Second email : UNIQUEMENT le mot de passe
                $passwordResult = ApiService::sendEmail(
                    $email,
                    $name,
                    'lostpwd',
                    ['username' => $username, 'password' => $password]
                );

                // Réponse selon le résultat de l'envoi d'email
                if ($welcomeResult === 0 || $passwordResult === 0) {
                    Response::json([
                        'status' => 'warning',
                        'message' => 'Compte créé avec succès mais impossible de vous envoyer tous les emails. ' .
                            'Rendez-vous sur la page de login et choisissez mot de passe perdu pour recevoir votre mot de passe.'
                    ], 201);
                } else {
                    Response::json([
                        'status' => 'success',
                        'message' => 'Votre compte a bien été créé et vous recevrez par email votre identifiant et mot de passe'
                    ], 201);
                }
            } catch (Exception $e) {
                // Vérifier si une transaction est active avant de faire rollback
                if ($this->db->inTransaction()) {
                    $this->db->rollBack();
                }

                // Construire un message d'erreur détaillé pour le logging
                $errorDetails = [
                    'level' => 'error',
                    'exception_class' => get_class($e),
                    'error_message' => $e->getMessage(),
                    'error_code' => $e->getCode(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                    'email' => $email ?? 'non disponible',
                    'amicaleName' => $amicaleName ?? 'non disponible',
                    'postalCode' => $postalCode ?? 'non disponible',
                    'trace' => $e->getTraceAsString()
                ];

                // Si c'est une PDOException, ajouter les infos SQL
                if ($e instanceof PDOException) {
                    $errorDetails['pdo_error_info'] = $this->db->errorInfo();
                }

                LogService::log('Erreur lors de la création du compte GeoSector', $errorDetails);

                // Retourner un message utilisateur clair (ne pas exposer les détails techniques)
                $userMessage = 'Une erreur est survenue lors de la création du compte. Veuillez réessayer ou contacter le support.';

                Response::json([
                    'status' => 'error',
                    'message' => $userMessage
                ], 500);
                return;
            }
        } catch (PDOException $e) {
            LogService::log('Erreur serveur lors de l\'inscription GeoSector', [
                'level' => 'error',
                'error' => $e->getMessage(),
                'code' => $e->getCode(),
                'trace' => $e->getTraceAsString()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Erreur lors de la création du compte. Veuillez réessayer.'
            ], 500);
        } catch (Exception $e) {
            LogService::log('Erreur inattendue lors de l\'inscription GeoSector', [
                'level' => 'error',
                'error' => $e->getMessage()
            ]);
            Response::json([
                'status' => 'error',
                'message' => 'Une erreur inattendue est survenue. Veuillez réessayer.'
            ], 500);
        }
    }

    // Méthodes auxiliaires

    public function logout(): void {
        $userId = Session::getUserId();
        $entityId = Session::getEntityId();

        Session::logout();

        // Log de déconnexion
        if ($userId) {
            EventLogService::logLogout($userId, $entityId, 0);
        }

        // Retourner une réponse standardisée
        Response::json([
            'status' => 'success',
            'message' => 'Déconnexion réussie'
        ]);
    }

    /**
     * Recherche les coordonnées GPS d'une caserne de pompiers dans une ville donnée
     * 
     * @param string $postalCode Le code postal de la ville
     * @param string $cityName Le nom de la ville
     * @return array|null Tableau associatif contenant les coordonnées GPS (lat, lng) ou null si aucune caserne trouvée
     * @throws Exception En cas d'erreur lors de la requête API
     */
    private function findFireStationCoordinates(string $postalCode, string $cityName): ?array {
        // Mots-clés pour rechercher une caserne de pompiers
        $keywords = ['pompiers', 'sdis', 'sapeurs-pompiers', 'caserne', 'centre de secours'];

        // Formater la ville et le code postal pour la recherche
        $citySearch = urlencode($cityName . ' ' . $postalCode);

        // Créer un contexte avec timeout de 2 secondes
        $context = stream_context_create([
            'http' => [
                'timeout' => 2,
                'ignore_errors' => true
            ]
        ]);

        foreach ($keywords as $keyword) {
            // Construire l'URL de recherche pour l'API adresse.gouv.fr
            $searchUrl = "https://api-adresse.data.gouv.fr/search/?q=" . urlencode($keyword) . "+$citySearch&limit=5";

            // Effectuer la requête HTTP avec timeout
            $response = @file_get_contents($searchUrl, false, $context);

            if ($response === false) {
                LogService::log('Erreur lors de la requête à l\'API adresse.gouv.fr', [
                    'level' => 'error',
                    'url' => $searchUrl
                ]);
                continue; // Essayer avec le mot-clé suivant
            }

            // Décoder la réponse JSON
            $data = json_decode($response, true);

            if (!$data || !isset($data['features']) || empty($data['features'])) {
                continue; // Aucun résultat, essayer avec le mot-clé suivant
            }

            // Parcourir les résultats pour trouver une caserne de pompiers
            foreach ($data['features'] as $feature) {
                $properties = $feature['properties'] ?? [];
                $name = strtolower($properties['name'] ?? '');
                $label = strtolower($properties['label'] ?? '');

                // Vérifier si le résultat correspond à une caserne de pompiers
                if (
                    strpos($name, 'pompier') !== false ||
                    strpos($name, 'sdis') !== false ||
                    strpos($label, 'pompier') !== false ||
                    strpos($label, 'sdis') !== false ||
                    strpos($name, 'caserne') !== false ||
                    strpos($label, 'caserne') !== false ||
                    strpos($name, 'centre de secours') !== false ||
                    strpos($label, 'centre de secours') !== false
                ) {
                    // Extraire les coordonnées GPS
                    $coordinates = $feature['geometry']['coordinates'] ?? null;

                    if ($coordinates && count($coordinates) >= 2) {
                        // L'API retourne les coordonnées au format [longitude, latitude]
                        return [
                            'lng' => $coordinates[0],
                            'lat' => $coordinates[1]
                        ];
                    }
                }
            }
        }

        // Si aucune caserne trouvée, chercher simplement ville + code postal avec timeout
        $citySearch = urlencode($cityName . ' ' . $postalCode);
        $cityUrl = "https://api-adresse.data.gouv.fr/search/?q=$citySearch&limit=1";

        // Créer un contexte avec timeout de 2 secondes
        $context = stream_context_create([
            'http' => [
                'timeout' => 2,
                'ignore_errors' => true
            ]
        ]);

        $cityResponse = @file_get_contents($cityUrl, false, $context);

        if ($cityResponse !== false) {
            $cityData = json_decode($cityResponse, true);

            if ($cityData && isset($cityData['features'][0]['geometry']['coordinates'])) {
                $coordinates = $cityData['features'][0]['geometry']['coordinates'];

                LogService::log('Coordonnées GPS récupérées pour l\'adresse', [
                    'level' => 'info',
                    'city' => $cityName,
                    'postalCode' => $postalCode
                ]);

                return [
                    'lng' => $coordinates[0],
                    'lat' => $coordinates[1]
                ];
            }
        }

        // Aucune coordonnée trouvée
        LogService::log('Aucune coordonnée GPS trouvée (timeout ou adresse invalide)', [
            'level' => 'warning',
            'city' => $cityName,
            'postalCode' => $postalCode
        ]);

        return null;
    }
}
