<?php
namespace App\Controllers;

use Database;
use Response;
use App\Services\LogService;
use App\Services\EventLogService;
use App\Services\ApiService;
use App\Services\AddressService;
use App\Services\DepartmentBoundaryService;

require_once __DIR__ . '/../Services/EventLogService.php';
require_once __DIR__ . '/../Services/ApiService.php';

class SectorController
{
    private \PDO $db;
    private AddressService $addressService;
    private DepartmentBoundaryService $boundaryService;

    public function __construct()
    {
        $this->db = Database::getInstance();
        $this->addressService = new AddressService();
        $this->boundaryService = new DepartmentBoundaryService();
    }
    
    /**
     * GET /sectors - Récupérer tous les secteurs
     */
    public function index(): void
    {
        try {
            $entityId = $_SESSION['entity_id'] ?? null;
            
            if (!$entityId) {
                Response::json(['status' => 'error', 'message' => 'Entité non définie'], 400);
                return;
            }
            
            $query = "
                SELECT 
                    s.id,
                    s.libelle,
                    s.color,
                    s.sector,
                    o.fk_entite,
                    GROUP_CONCAT(ous.fk_user) as membres
                FROM ope_sectors s
                LEFT JOIN ope_users_sectors ous ON s.id = ous.fk_sector
                JOIN operations o ON s.fk_operation = o.id
                WHERE o.fk_entite = :entity_id
                GROUP BY s.id
                ORDER BY s.libelle
            ";
            
            $stmt = $this->db->prepare($query);
            $stmt->execute(['entity_id' => $entityId]);
            $sectors = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Convertir les membres en tableau d'entiers
            foreach ($sectors as &$sector) {
                if ($sector['membres']) {
                    $sector['membres'] = array_map('intval', explode(',', $sector['membres']));
                } else {
                    $sector['membres'] = [];
                }
            }
            
            Response::json(['status' => 'success', 'data' => $sectors]);
            
        } catch (\Exception $e) {
            LogService::error('Erreur lors de la récupération des secteurs', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la récupération des secteurs'], 500);
        }
    }
    
    /**
     * POST /sectors - Créer un nouveau secteur
     */
    public function create(): void
    {
        try {
            $data = json_decode(file_get_contents('php://input'), true);
            
            // Validation des données - Structure directe depuis Flutter
            if (!isset($data['libelle']) || !isset($data['color']) || !isset($data['sector'])) {
                Response::json(['status' => 'error', 'message' => 'Données du secteur manquantes'], 400);
                return;
            }
            
            $sectorData = $data;
            $entityId = $data['fk_entite'] ?? $_SESSION['entity_id'] ?? null;
            $operationId = $data['operation_id'] ?? null;
            $userId = $data['user_id'] ?? $_SESSION['user_id'] ?? null;
            $users = $data['users'] ?? [];
            
            if (!$entityId) {
                Response::json(['status' => 'error', 'message' => 'Entité non définie'], 400);
                return;
            }
            
            // Validation de l'opération
            if (!$operationId) {
                Response::json(['status' => 'error', 'message' => 'Opération non définie'], 400);
                return;
            }
            
            // Traitement des coordonnées pour validation
            $sector = $sectorData['sector'];
            $points = explode('#', rtrim($sector, '#'));
            $coordinates = [];
            
            foreach ($points as $point) {
                if (!empty($point)) {
                    list($lat, $lng) = explode('/', $point);
                    $coordinates[] = [floatval($lat), floatval($lng)]; // Format [lat, lng] pour AddressService
                }
            }
            
            if (count($coordinates) < 3) {
                Response::json(['status' => 'error', 'message' => 'Un secteur doit avoir au moins 3 points'], 400);
                return;
            }
            
            // Vérifier que le secteur est dans le département de l'entité
            try {
                // Récupérer le code postal de l'entité pour en déduire le département
                $deptQuery = "SELECT code_postal, encrypted_name FROM entites WHERE id = :entity_id";
                $deptStmt = $this->db->prepare($deptQuery);
                $deptStmt->execute(['entity_id' => $entityId]);
                $entity = $deptStmt->fetch();
                
                if (!$entity || !$entity['code_postal']) {
                    Response::json(['status' => 'error', 'message' => 'Code postal de l\'entité non défini'], 400);
                    return;
                }
                
                // Extraire le département du code postal (2 premiers caractères)
                $codePostal = $entity['code_postal'];
                if (strlen($codePostal) === 4) {
                    $codePostal = '0' . $codePostal; // Ajouter le 0 devant si nécessaire
                }
                $departement = substr($codePostal, 0, 2);
                
                // Identifier tous les départements touchés par le secteur
                $departmentsTouched = $this->boundaryService->getDepartmentsForSector($coordinates);
                
                if (empty($departmentsTouched)) {
                    LogService::warning('Aucun département trouvé pour le secteur', [
                        'libelle' => $data['libelle'],
                        'entity_id' => $entityId,
                        'entity_dept' => $departement
                    ]);
                }
            } catch (\Exception $e) {
                LogService::warning('Impossible de vérifier les limites départementales', [
                    'error' => $e->getMessage(),
                    'libelle' => $data['libelle']
                ]);
            }
            
            // Récupérer les adresses dans le polygone
            try {
                $addressCount = $this->addressService->countAddressesInPolygon($coordinates, $entityId);
            } catch (\Exception $e) {
                LogService::warning('Impossible de récupérer les adresses du secteur', [
                    'error' => $e->getMessage(),
                    'libelle' => $data['libelle'],
                    'entity_id' => $entityId
                ]);
                $addressCount = 0;
            }
            
            $this->db->beginTransaction();
            
            // Insertion du secteur
            $query = "INSERT INTO ope_sectors (libelle, color, sector, fk_operation) VALUES (:libelle, :color, :sector, :operation_id)";
            $stmt = $this->db->prepare($query);
            $stmt->execute([
                'libelle' => $sectorData['libelle'],
                'color' => $sectorData['color'],
                'sector' => $sectorData['sector'],
                'operation_id' => $operationId
            ]);
            
            $sectorId = $this->db->lastInsertId();
            
            // Affectation des users si fournis
            if (!empty($users)) {
                $queryMember = "INSERT INTO ope_users_sectors (fk_operation, fk_user, fk_sector, created_at, updated_at, fk_user_creat, chk_active)
                               VALUES (:operation_id, :user_id, :sector_id, NOW(), NOW(), :user_creat, 1)";
                $stmtMember = $this->db->prepare($queryMember);

                foreach ($users as $memberId) {
                    // $memberId est DÉJÀ ope_users.id (envoyé par Flutter)
                    // Vérifier que cet ope_users.id existe et appartient bien à l'opération
                    $stmtOpeUser = $this->db->prepare('
                        SELECT id FROM ope_users
                        WHERE id = ? AND fk_operation = ?
                    ');
                    $stmtOpeUser->execute([$memberId, $operationId]);
                    $opeUserId = $stmtOpeUser->fetchColumn();

                    if (!$opeUserId) {
                        LogService::warning('ope_users.id non trouvé pour cette opération', [
                            'ope_users_id' => $memberId,
                            'operation_id' => $operationId
                        ]);
                        continue;
                    }

                    $stmtMember->execute([
                        'operation_id' => $operationId,
                        'user_id' => $opeUserId,
                        'sector_id' => $sectorId,
                        'user_creat' => $userId
                    ]);
                }
            }
            
            // D'abord, chercher et intégrer les passages orphelins (fk_sector=NULL) dans le nouveau secteur
            $passagesIntegrated = 0;
            $addressesToExclude = []; // Adresses à ne pas créer en doublon
            
            try {
                // Créer le polygone SQL pour la vérification
                $polygonPoints = [];
                foreach ($coordinates as $coord) {
                    $polygonPoints[] = $coord[1] . ' ' . $coord[0]; // longitude latitude
                }
                $polygonPoints[] = $polygonPoints[0]; // Fermer le polygone
                $polygonString = 'POLYGON((' . implode(',', $polygonPoints) . '))';
                
                // Chercher les passages orphelins dans le périmètre du secteur
                $orphanQuery = "SELECT id, fk_adresse, gps_lat, gps_lng 
                               FROM ope_pass 
                               WHERE fk_operation = :operation_id 
                                 AND fk_sector IS NULL 
                                 AND gps_lat IS NOT NULL 
                                 AND gps_lng IS NOT NULL
                                 AND ST_Contains(ST_GeomFromText(:polygon, 4326), 
                                                 POINT(CAST(gps_lng AS DECIMAL(10,8)), 
                                                       CAST(gps_lat AS DECIMAL(10,8))))";
                
                $orphanStmt = $this->db->prepare($orphanQuery);
                $orphanStmt->execute([
                    'operation_id' => $operationId,
                    'polygon' => $polygonString
                ]);
                
                $orphanPassages = $orphanStmt->fetchAll();
                
                if (!empty($orphanPassages)) {
                    $updateOrphanQuery = "UPDATE ope_pass SET fk_sector = :sector_id WHERE id = :passage_id";
                    $updateOrphanStmt = $this->db->prepare($updateOrphanQuery);
                    
                    foreach ($orphanPassages as $orphan) {
                        $updateOrphanStmt->execute([
                            'sector_id' => $sectorId,
                            'passage_id' => $orphan['id']
                        ]);
                        $passagesIntegrated++;
                        
                        // Si le passage a un fk_adresse, l'ajouter à la liste des exclusions
                        if (!empty($orphan['fk_adresse'])) {
                            $addressesToExclude[] = $orphan['fk_adresse'];
                        }
                    }
                    
                }
            } catch (\Exception $e) {
                LogService::warning('Erreur lors de la récupération des passages orphelins', [
                    'sector_id' => $sectorId,
                    'error' => $e->getMessage()
                ]);
            }
            
            // Stocker les adresses du secteur
            $passagesCreated = 0; // Initialiser le compteur de passages
            try {
                $addresses = $this->addressService->getAddressesInPolygon($coordinates, $entityId);

                // Enrichir les adresses avec les données bâtiments
                $addresses = $this->addressService->enrichAddressesWithBuildings($addresses, $entityId);

                if (!empty($addresses)) {
                    $queryAddress = "INSERT INTO sectors_adresses (
                        fk_sector, fk_adresse, numero, rue, rue_bis, cp, ville, gps_lat, gps_lng,
                        fk_batiment, fk_habitat, nb_niveau, nb_log, residence, alt_sol
                    ) VALUES (
                        :sector_id, :address_id, :numero, :rue, :rue_bis, :cp, :ville, :gps_lat, :gps_lng,
                        :fk_batiment, :fk_habitat, :nb_niveau, :nb_log, :residence, :alt_sol
                    )";
                    $stmtAddress = $this->db->prepare($queryAddress);

                    foreach ($addresses as $address) {
                        // Extraire le rue_bis si présent (généralement vide)
                        $rueBis = '';

                        $stmtAddress->execute([
                            'sector_id' => $sectorId,
                            'address_id' => $address['id'],
                            'numero' => $address['numero'],
                            'rue' => $address['voie'],
                            'rue_bis' => $rueBis,
                            'cp' => $address['code_postal'],
                            'ville' => $address['commune'],
                            'gps_lat' => $address['latitude'],
                            'gps_lng' => $address['longitude'],
                            'fk_batiment' => $address['fk_batiment'] ?? null,
                            'fk_habitat' => $address['fk_habitat'] ?? 1,
                            'nb_niveau' => $address['nb_niveau'] ?? null,
                            'nb_log' => $address['nb_log'] ?? null,
                            'residence' => $address['residence'] ?? '',
                            'alt_sol' => $address['alt_sol'] ?? null
                        ]);
                    }
                    
                    
                    // Créer les passages pour chaque adresse
                    if (!empty($users)) {
                        // Récupérer ope_users.id pour le premier utilisateur
                        // $users[0] est DÉJÀ ope_users.id (envoyé par Flutter)
                        $stmtFirstOpeUser = $this->db->prepare('
                            SELECT id FROM ope_users
                            WHERE id = ? AND fk_operation = ?
                        ');
                        $stmtFirstOpeUser->execute([$users[0], $operationId]);
                        $firstOpeUserId = $stmtFirstOpeUser->fetchColumn();

                        if (!$firstOpeUserId) {
                            LogService::warning('Premier ope_users.id non trouvé pour cette opération', [
                                'ope_users_id' => $users[0],
                                'operation_id' => $operationId
                            ]);
                            // Pas de création de passages sans utilisateur valide dans ope_users
                        } else {
                            $passageQuery = "INSERT INTO ope_pass (
                                fk_operation, fk_sector, fk_user, fk_adresse,
                                numero, rue, rue_bis, ville, residence, appt, fk_habitat,
                                gps_lat, gps_lng, fk_type, nb_passages, encrypted_name,
                                created_at, fk_user_creat, chk_active
                            ) VALUES (
                                :operation_id, :sector_id, :user_id, :fk_adresse,
                                :numero, :rue, :rue_bis, :ville, :residence, :appt, :fk_habitat,
                                :gps_lat, :gps_lng, 2, 0, '',
                                NOW(), :user_creat, 1
                            )";
                            $passageStmt = $this->db->prepare($passageQuery);

                            $passagesCreated = 0;
                            foreach ($addresses as $address) {
                                // Vérifier si cette adresse n'est pas déjà utilisée par un passage orphelin
                                if (in_array($address['id'], $addressesToExclude)) {
                                    continue; // Passer à l'adresse suivante
                                }

                                try {
                                    // Extraire le rue_bis si présent (généralement vide)
                                    $rueBis = '';

                                    // Déterminer le nombre de passages à créer
                                    $fkHabitat = $address['fk_habitat'] ?? 1;
                                    $nbLog = ($fkHabitat == 2 && isset($address['nb_log'])) ? (int)$address['nb_log'] : 1;
                                    $residence = $address['residence'] ?? '';

                                    // IMPORTANT : Uniformisation GPS pour les immeubles
                                    // Tous les passages d'une même adresse partagent les mêmes coordonnées GPS
                                    // Issues de la table adresses enrichie (gps_lat, gps_lng)
                                    $gpsLat = $address['latitude'];
                                    $gpsLng = $address['longitude'];

                                    // Créer 1 passage pour maison individuelle, nb_log passages pour immeuble
                                    for ($i = 1; $i <= $nbLog; $i++) {
                                        $appt = ($fkHabitat == 2) ? (string)$i : ''; // Numéro d'appartement pour immeubles

                                        $passageStmt->execute([
                                            'operation_id' => $operationId,
                                            'sector_id' => $sectorId,
                                            'user_id' => $firstOpeUserId,
                                            'fk_adresse' => $address['id'],
                                            'numero' => $address['numero'],
                                            'rue' => $address['voie'],
                                            'rue_bis' => $rueBis,
                                            'ville' => $address['commune'],
                                            'residence' => $residence,
                                            'appt' => $appt,
                                            'fk_habitat' => $fkHabitat,
                                            'gps_lat' => $gpsLat,
                                            'gps_lng' => $gpsLng,
                                            'user_creat' => $userId
                                        ]);
                                        $passagesCreated++;
                                    }

                                    // Log pour vérifier l'uniformisation GPS (surtout pour immeubles)
                                    if ($fkHabitat == 2 && $nbLog > 1) {
                                        LogService::info('[SectorController] Création passages immeuble avec GPS uniformisés', [
                                            'address_id' => $address['id'],
                                            'nb_passages' => $nbLog,
                                            'gps_lat' => $gpsLat,
                                            'gps_lng' => $gpsLng,
                                            'residence' => $residence
                                        ]);
                                    }
                                } catch (\Exception $e) {
                                    LogService::warning('Erreur lors de la création d\'un passage', [
                                        'address_id' => $address['id'],
                                        'error' => $e->getMessage()
                                    ]);
                                }
                            }
                        }
                    }
                }
            } catch (\Exception $e) {
                // En cas d'erreur avec les adresses, on ne bloque pas la création du secteur
                LogService::error('Erreur lors du stockage des adresses du secteur', [
                    'sector_id' => $sectorId,
                    'error' => $e->getMessage(),
                    'entity_id' => $entityId
                ]);
            }

            $this->db->commit();

            // Log de création du secteur
            EventLogService::logSectorCreated(
                (int)$sectorId,
                (int)$operationId,
                $sectorData['libelle']
            );

            // Préparer les données de réponse
            $responseData = [
                'sector_id' => $sectorId
            ];
            
            // Récupérer tous les passages du secteur (créés + intégrés)
            $totalPassages = (isset($passagesCreated) ? $passagesCreated : 0) + $passagesIntegrated;
            if ($totalPassages > 0) {
                $passagesQuery = "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_sector = :sector_id 
                                 ORDER BY id";
                $passagesStmt = $this->db->prepare($passagesQuery);
                $passagesStmt->execute(['sector_id' => $sectorId]);
                $passages = $passagesStmt->fetchAll(\PDO::FETCH_ASSOC);
                
                // Déchiffrer les données sensibles des passages
                $passagesDecrypted = [];
                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']);
                    
                    $passagesDecrypted[] = $passage;
                }
                
                $responseData['passages_sector'] = $passagesDecrypted;
                $responseData['passages_integrated'] = $passagesIntegrated;
                $responseData['passages_created'] = isset($passagesCreated) ? $passagesCreated : 0;
            } else {
                $responseData['passages_sector'] = [];
                $responseData['passages_integrated'] = 0;
                $responseData['passages_created'] = 0;
            }
            
            // Récupérer les users affectés
            $usersQuery = "SELECT u.id, ou.id as ope_user_id, u.first_name, u.sect_name, u.encrypted_name, ous.fk_sector
                          FROM ope_users_sectors ous
                          JOIN ope_users ou ON ous.fk_user = ou.id
                          JOIN users u ON ou.fk_user = u.id
                          WHERE ous.fk_sector = :sector_id";
            $usersStmt = $this->db->prepare($usersQuery);
            $usersStmt->execute(['sector_id' => $sectorId]);
            $usersSectors = $usersStmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Déchiffrer les noms des utilisateurs
            $responseData['users_sectors'] = [];
            foreach ($usersSectors as $userSector) {
                $userData = [
                    'user_id' => $userSector['id'],
                    'ope_user_id' => $userSector['ope_user_id'],
                    'first_name' => $userSector['first_name'] ?? '',
                    'sect_name' => $userSector['sect_name'] ?? '',
                    'fk_sector' => $userSector['fk_sector'],
                    'name' => ''
                ];
                
                // Déchiffrer le nom
                if (!empty($userSector['encrypted_name'])) {
                    $userData['name'] = ApiService::decryptData($userSector['encrypted_name']);
                }
                
                $responseData['users_sectors'][] = $userData;
            }
            
            LogService::info('Secteur créé', [
                'sector_id' => $sectorId,
                'libelle' => $sectorData['libelle'],
                'entity_id' => $entityId,
                'user_id' => $userId,
                'passages_created' => isset($passagesCreated) ? $passagesCreated : 0,
                'passages_integrated' => $passagesIntegrated,
                'users_assigned' => count($users)
            ]);
            
            // Construire la réponse selon la norme de l'API (sans groupe "data")
            $response = [
                'status' => 'success',
                'message' => 'Secteur créé avec succès',
                'sector' => [
                    'id' => $sectorId,
                    'libelle' => $sectorData['libelle'],
                    'color' => $sectorData['color'],
                    'sector' => $sectorData['sector']
                ]
            ];
            
            // Ajouter les autres données directement à la racine
            if (isset($responseData['passages_sector'])) {
                $response['passages_sector'] = $responseData['passages_sector'];
            }
            if (isset($responseData['passages_integrated'])) {
                $response['passages_integrated'] = $responseData['passages_integrated'];
            }
            if (isset($responseData['passages_created'])) {
                $response['passages_created'] = $responseData['passages_created'];
            }
            if (isset($responseData['users_sectors'])) {
                $response['users_sectors'] = $responseData['users_sectors'];
            }
            
            Response::json($response, 201);
            
        } catch (\Exception $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            LogService::error('Erreur lors de la création du secteur', [
                'error' => $e->getMessage(),
                'data' => $data ?? null
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la création du secteur'], 500);
        }
    }
    
    /**
     * PUT /sectors/{id} - Modifier un secteur
     */
    public function update($id): void
    {
        try {
            $data = json_decode(file_get_contents('php://input'), true);
            $entityId = $_SESSION['entity_id'] ?? null;

            if (!$entityId) {
                Response::json(['status' => 'error', 'message' => 'Entité non définie'], 400);
                return;
            }

            // Vérifier que le secteur appartient à l'entité
            $checkQuery = "SELECT s.id, s.fk_operation, s.libelle
                          FROM ope_sectors s
                          JOIN operations o ON s.fk_operation = o.id
                          WHERE s.id = :id AND o.fk_entite = :entity_id";
            $checkStmt = $this->db->prepare($checkQuery);
            $checkStmt->execute(['id' => $id, 'entity_id' => $entityId]);

            $existingSector = $checkStmt->fetch();
            if (!$existingSector) {
                Response::json(['status' => 'error', 'message' => 'Secteur non trouvé'], 404);
                return;
            }

            $operationId = $existingSector['fk_operation'];
            
            $this->db->beginTransaction();
            
            // Mise à jour du secteur
            $updateFields = [];
            $params = ['id' => $id];
            
            if (isset($data['libelle'])) {
                $updateFields[] = 'libelle = :libelle';
                $params['libelle'] = $data['libelle'];
            }
            
            if (isset($data['color'])) {
                $updateFields[] = 'color = :color';
                $params['color'] = $data['color'];
            }
            
            if (isset($data['sector'])) {
                $updateFields[] = 'sector = :sector';
                $params['sector'] = $data['sector'];
            }
            
            if (!empty($updateFields)) {
                $query = "UPDATE ope_sectors SET " . implode(', ', $updateFields) . " WHERE id = :id";
                $stmt = $this->db->prepare($query);
                $stmt->execute($params);
            }
            
            // Gestion des membres (reçus comme 'users' depuis Flutter)
            if (isset($data['users'])) {
                LogService::info('[UPDATE USERS] Début modification des membres', [
                    'sector_id' => $id,
                    'users_demandes' => $data['users'],
                    'nb_users' => count($data['users'])
                ]);

                // Récupérer l'opération du secteur pour l'INSERT
                $opQuery = "SELECT fk_operation FROM ope_sectors WHERE id = :sector_id";
                LogService::info('[UPDATE USERS] SQL - Récupération fk_operation', [
                    'query' => $opQuery,
                    'params' => ['sector_id' => $id]
                ]);
                $opStmt = $this->db->prepare($opQuery);
                $opStmt->execute(['sector_id' => $id]);
                $operationId = $opStmt->fetch()['fk_operation'];
                LogService::info('[UPDATE USERS] fk_operation récupéré', [
                    'operation_id' => $operationId
                ]);

                // Supprimer les affectations existantes
                $deleteQuery = "DELETE FROM ope_users_sectors WHERE fk_sector = :sector_id";
                LogService::info('[UPDATE USERS] SQL - Suppression des anciens membres', [
                    'query' => $deleteQuery,
                    'params' => ['sector_id' => $id]
                ]);
                $deleteStmt = $this->db->prepare($deleteQuery);
                $deleteStmt->execute(['sector_id' => $id]);
                $deletedCount = $deleteStmt->rowCount();
                LogService::info('[UPDATE USERS] Membres supprimés', [
                    'nb_deleted' => $deletedCount
                ]);

                // Ajouter les nouvelles affectations
                if (!empty($data['users'])) {
                    $insertQuery = "INSERT INTO ope_users_sectors (fk_operation, fk_user, fk_sector, created_at, updated_at, fk_user_creat, chk_active)
                                   VALUES (:operation_id, :user_id, :sector_id, NOW(), NOW(), :user_creat, 1)";
                    LogService::info('[UPDATE USERS] SQL - Requête INSERT préparée', [
                        'query' => $insertQuery
                    ]);
                    $insertStmt = $this->db->prepare($insertQuery);

                    $insertedUsers = [];
                    $failedUsers = [];
                    foreach ($data['users'] as $memberId) {
                        try {
                            // $memberId est DÉJÀ ope_users.id (envoyé par Flutter)
                            // Vérifier que cet ope_users.id existe et appartient bien à l'opération
                            $stmtOpeUser = $this->db->prepare('
                                SELECT id FROM ope_users
                                WHERE id = ? AND fk_operation = ?
                            ');
                            $stmtOpeUser->execute([$memberId, $operationId]);
                            $opeUserId = $stmtOpeUser->fetchColumn();

                            if (!$opeUserId) {
                                LogService::warning('[UPDATE USERS] ope_users.id non trouvé pour cette opération', [
                                    'ope_users_id' => $memberId,
                                    'operation_id' => $operationId
                                ]);
                                $failedUsers[] = $memberId;
                                continue;
                            }

                            $params = [
                                'operation_id' => $operationId,
                                'user_id' => $opeUserId,
                                'sector_id' => $id,
                                'user_creat' => $_SESSION['user_id'] ?? null
                            ];
                            LogService::info('[UPDATE USERS] SQL - INSERT user', [
                                'params' => $params
                            ]);
                            $insertStmt->execute($params);
                            $insertedUsers[] = $memberId;
                            LogService::info('[UPDATE USERS] User inséré avec succès', [
                                'user_id' => $memberId
                            ]);
                        } catch (\PDOException $e) {
                            $failedUsers[] = $memberId;
                            LogService::warning('[UPDATE USERS] ERREUR insertion user', [
                                'sector_id' => $id,
                                'user_id' => $memberId,
                                'error' => $e->getMessage(),
                                'error_code' => $e->getCode()
                            ]);
                        }
                    }

                    LogService::info('[UPDATE USERS] Résultat des insertions', [
                        'users_demandes' => $data['users'],
                        'users_inseres' => $insertedUsers,
                        'users_echoues' => $failedUsers,
                        'nb_succes' => count($insertedUsers),
                        'nb_echecs' => count($failedUsers)
                    ]);
                }
            }
            
            // Gérer les passages si le secteur a changé ET si chk_adresses_change = 1
            $passageCounters = [
                'passages_orphaned' => 0,
                'passages_updated' => 0,
                'passages_created' => 0,
                'passages_kept' => 0
            ];

            // chk_adresses_change : 0=ne pas toucher aux adresses/passages, 1=recalculer (défaut)
            $chkAdressesChange = $data['chk_adresses_change'] ?? 1;

            if (isset($data['sector']) && $chkAdressesChange == 0) {
                LogService::info('[UPDATE] Modification secteur sans recalcul adresses/passages', [
                    'sector_id' => $id,
                    'chk_adresses_change' => $chkAdressesChange
                ]);
            }

            if (isset($data['sector']) && $chkAdressesChange == 1) {
                // Mettre à jour les adresses du secteur AVANT de traiter les passages
                try {
                    // Supprimer les anciennes adresses
                    $deleteAddressQuery = "DELETE FROM sectors_adresses WHERE fk_sector = :sector_id";
                    $deleteAddressStmt = $this->db->prepare($deleteAddressQuery);
                    $deleteAddressStmt->execute(['sector_id' => $id]);
                    
                    // Traiter les nouvelles coordonnées
                    $points = explode('#', rtrim($data['sector'], '#'));
                    $coordinates = [];
                    
                    foreach ($points as $point) {
                        if (!empty($point)) {
                            list($lat, $lng) = explode('/', $point);
                            $coordinates[] = [floatval($lat), floatval($lng)];
                        }
                    }
                    
                    // Récupérer et stocker les nouvelles adresses
                    LogService::info('[UPDATE] Récupération des adresses', [
                        'sector_id' => $id,
                        'entity_id' => $entityId,
                        'nb_points' => count($coordinates)
                    ]);
                    
                    $addresses = $this->addressService->getAddressesInPolygon($coordinates, $entityId);

                    // Enrichir les adresses avec les données bâtiments
                    $addresses = $this->addressService->enrichAddressesWithBuildings($addresses, $entityId);

                    LogService::info('[UPDATE] Adresses récupérées', [
                        'sector_id' => $id,
                        'nb_addresses' => count($addresses)
                    ]);

                    if (!empty($addresses)) {
                        $queryAddress = "INSERT INTO sectors_adresses (
                            fk_sector, fk_adresse, numero, rue, cp, ville, gps_lat, gps_lng,
                            fk_batiment, fk_habitat, nb_niveau, nb_log, residence, alt_sol
                        ) VALUES (
                            :sector_id, :address_id, :numero, :rue, :cp, :ville, :gps_lat, :gps_lng,
                            :fk_batiment, :fk_habitat, :nb_niveau, :nb_log, :residence, :alt_sol
                        )";
                        $stmtAddress = $this->db->prepare($queryAddress);

                        foreach ($addresses as $address) {
                            $stmtAddress->execute([
                                'sector_id' => $id,
                                'address_id' => $address['id'],
                                'numero' => $address['numero'],
                                'rue' => $address['voie'],
                                'cp' => $address['code_postal'],
                                'ville' => $address['commune'],
                                'gps_lat' => $address['latitude'],
                                'gps_lng' => $address['longitude'],
                                'fk_batiment' => $address['fk_batiment'] ?? null,
                                'fk_habitat' => $address['fk_habitat'] ?? 1,
                                'nb_niveau' => $address['nb_niveau'] ?? null,
                                'nb_log' => $address['nb_log'] ?? null,
                                'residence' => $address['residence'] ?? '',
                                'alt_sol' => $address['alt_sol'] ?? null
                            ]);
                        }
                        
                        LogService::info('[UPDATE] Adresses stockées dans sectors_adresses', [
                            'sector_id' => $id,
                            'nb_stored' => count($addresses)
                        ]);
                    } else {
                        LogService::warning('[UPDATE] Aucune adresse trouvée pour le secteur', [
                            'sector_id' => $id,
                            'entity_id' => $entityId
                        ]);
                    }
                    
                    // Vérifier si c'est un problème de connexion à la base d'adresses
                    if (!$this->addressService->isConnected()) {
                        LogService::warning('[UPDATE] Base d\'adresses non accessible - passages créés sans adresses', [
                            'sector_id' => $id
                        ]);
                    }
                } catch (\Exception $e) {
                    LogService::error('[UPDATE] Erreur lors de la mise à jour des adresses du secteur', [
                        'sector_id' => $id,
                        'error' => $e->getMessage()
                    ]);
                }
                
                // Maintenant que les adresses sont mises à jour, traiter les passages
                LogService::info('[UPDATE] Début mise à jour des passages', ['sector_id' => $id]);
                $passageCounters = $this->updatePassagesForSector($id, $data['sector']);
            }

            // Commit des modifications (users et/ou secteur)
            $this->db->commit();

            // Log de mise à jour du secteur
            $changes = [];
            if (isset($data['libelle'])) {
                $changes['libelle'] = ['new' => $data['libelle']];
            }
            if (isset($data['color'])) {
                $changes['color'] = ['new' => $data['color']];
            }
            if (isset($data['sector'])) {
                $changes['sector'] = true; // Polygon modifié
            }
            if (isset($data['users'])) {
                $changes['users'] = true; // Affectation modifiée
            }

            if (!empty($changes)) {
                EventLogService::logSectorUpdated((int)$id, (int)$operationId, $changes);
            }

            // Récupérer le secteur mis à jour
            $query = "
                SELECT
                    s.id,
                    s.libelle,
                    s.color,
                    s.sector
                FROM ope_sectors s
                WHERE s.id = :id
            ";

            $stmt = $this->db->prepare($query);
            $stmt->execute(['id' => $id]);
            $sector = $stmt->fetch(\PDO::FETCH_ASSOC);

            // Récupérer les passages UNIQUEMENT si chk_adresses_change = 1
            $passagesDecrypted = [];
            if ($chkAdressesChange == 1) {
                // Récupérer tous les passages du secteur
                $passagesQuery = "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_sector = :sector_id
                                 ORDER BY id";
                $passagesStmt = $this->db->prepare($passagesQuery);
                $passagesStmt->execute(['sector_id' => $id]);
                $passages = $passagesStmt->fetchAll(\PDO::FETCH_ASSOC);

                // Déchiffrer les données sensibles des passages
                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']);

                    $passagesDecrypted[] = $passage;
                }
            }
            
            // Récupérer les users affectés (avec READ UNCOMMITTED pour forcer la lecture des données fraîches)
            $usersQuery = "SELECT u.id, ou.id as ope_user_id, u.first_name, u.sect_name, u.encrypted_name, ous.fk_sector
                          FROM ope_users_sectors ous
                          JOIN ope_users ou ON ous.fk_user = ou.id
                          JOIN users u ON ou.fk_user = u.id
                          WHERE ous.fk_sector = :sector_id
                          ORDER BY u.id";

            LogService::info('[UPDATE USERS] SQL - Récupération finale des users', [
                'query' => $usersQuery,
                'params' => ['sector_id' => $id]
            ]);

            $usersStmt = $this->db->prepare($usersQuery);
            $usersStmt->execute(['sector_id' => $id]);
            $usersSectors = $usersStmt->fetchAll(\PDO::FETCH_ASSOC);

            $userIds = array_column($usersSectors, 'id');
            LogService::info('[UPDATE USERS] Users récupérés après commit', [
                'sector_id' => $id,
                'users_ids' => $userIds,
                'nb_users' => count($userIds),
                'users_demandes_initialement' => $data['users'] ?? []
            ]);
            
            // Déchiffrer les noms des utilisateurs
            $usersDecrypted = [];
            foreach ($usersSectors as $userSector) {
                $userData = [
                    'user_id' => $userSector['id'],
                    'ope_user_id' => $userSector['ope_user_id'],
                    'first_name' => $userSector['first_name'] ?? '',
                    'sect_name' => $userSector['sect_name'] ?? '',
                    'fk_sector' => $userSector['fk_sector'],
                    'name' => ''
                ];
                
                // Déchiffrer le nom
                if (!empty($userSector['encrypted_name'])) {
                    $userData['name'] = ApiService::decryptData($userSector['encrypted_name']);
                }
                
                $usersDecrypted[] = $userData;
            }
            
            LogService::info('Secteur modifié', [
                'sector_id' => $id,
                'updates' => array_keys($data),
                'passage_counters' => $passageCounters,
                'user_id' => $_SESSION['user_id'] ?? null
            ]);
            
            // Construire la réponse identique à create avec les compteurs supplémentaires
            $response = [
                'status' => 'success',
                'message' => 'Secteur modifié avec succès',
                'sector' => $sector,
                'passages_sector' => $passagesDecrypted,
                'passages_orphaned' => $passageCounters['passages_orphaned'],
                'passages_deleted' => $passageCounters['passages_deleted'],
                'passages_updated' => $passageCounters['passages_updated'],
                'passages_created' => $passageCounters['passages_created'],
                'passages_total' => count($passagesDecrypted),
                'users_sectors' => $usersDecrypted
            ];
            
            Response::json($response);
            
        } catch (\Exception $e) {
            // Vérifier si une transaction est active avant de faire un rollback
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            LogService::error('Erreur lors de la modification du secteur', [
                'sector_id' => $id,
                'error' => $e->getMessage()
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la modification du secteur'], 500);
        }
    }
    
    /**
     * GET /sectors/{id}/addresses - Récupérer les adresses d'un secteur
     */
    public function getAddresses($id): void
    {
        try {
            $entityId = $_SESSION['entity_id'] ?? null;
            
            if (!$entityId) {
                Response::json(['status' => 'error', 'message' => 'Entité non définie'], 400);
                return;
            }
            
            // Vérifier que le secteur appartient à l'entité
            $checkQuery = "SELECT s.id 
                          FROM ope_sectors s
                          JOIN operations o ON s.fk_operation = o.id
                          WHERE s.id = :id AND o.fk_entite = :entity_id";
            $checkStmt = $this->db->prepare($checkQuery);
            $checkStmt->execute(['id' => $id, 'entity_id' => $entityId]);
            
            if (!$checkStmt->fetch()) {
                Response::json(['status' => 'error', 'message' => 'Secteur non trouvé'], 404);
                return;
            }
            
            // Récupérer les adresses du secteur
            $query = "
                SELECT 
                    fk_address as id,
                    numero,
                    voie,
                    code_postal,
                    commune,
                    latitude,
                    longitude
                FROM sectors_adresses
                WHERE fk_sector = :sector_id
                ORDER BY commune, voie, CAST(numero AS UNSIGNED)
            ";
            
            $stmt = $this->db->prepare($query);
            $stmt->execute(['sector_id' => $id]);
            $addresses = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Compter le total
            $countQuery = "SELECT COUNT(*) as total FROM sectors_adresses WHERE fk_sector = :sector_id";
            $countStmt = $this->db->prepare($countQuery);
            $countStmt->execute(['sector_id' => $id]);
            $total = $countStmt->fetch()['total'];
            
            Response::json([
                'status' => 'success',
                'data' => $addresses,
                'total' => $total
            ]);
            
        } catch (\Exception $e) {
            LogService::error('Erreur lors de la récupération des adresses du secteur', [
                'sector_id' => $id,
                'error' => $e->getMessage()
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la récupération des adresses'], 500);
        }
    }
    
    /**
     * DELETE /sectors/{id} - Supprimer un secteur
     */
    public function delete($id): void
    {
        try {
            // Récupérer les données de la requête si présentes (pour les API stateless)
            $data = json_decode(file_get_contents('php://input'), true) ?? [];
            $entityId = $data['fk_entite'] ?? $_SESSION['entity_id'] ?? null;
            
            if (!$entityId) {
                Response::json(['status' => 'error', 'message' => 'Entité non définie'], 400);
                return;
            }
            
            // Vérifier que le secteur existe et récupérer ses informations
            $checkQuery = "SELECT s.id, s.libelle, s.fk_operation, o.fk_entite
                          FROM ope_sectors s
                          JOIN operations o ON s.fk_operation = o.id
                          WHERE s.id = :id";
            $checkStmt = $this->db->prepare($checkQuery);
            $checkStmt->execute(['id' => $id]);
            $sector = $checkStmt->fetch();

            if (!$sector || $sector['fk_entite'] != $entityId) {
                Response::json(['status' => 'error', 'message' => 'Secteur non trouvé ou non autorisé'], 404);
                return;
            }

            $operationId = $sector['fk_operation'];
            
            $this->db->beginTransaction();
            
            // Compter les passages à supprimer (fk_type=2 et encrypted_name vide)
            $countDeleteQuery = "SELECT COUNT(*) as count 
                                FROM ope_pass 
                                WHERE fk_sector = :sector_id 
                                  AND fk_type = 2 
                                  AND (encrypted_name IS NULL OR encrypted_name = '')";
            $countDeleteStmt = $this->db->prepare($countDeleteQuery);
            $countDeleteStmt->execute(['sector_id' => $id]);
            $passagesToDelete = $countDeleteStmt->fetch()['count'];
            
            // Récupérer les passages à réaffecter avant de les modifier
            $getPassagesToUpdateQuery = "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_sector = :sector_id 
                                          AND NOT (fk_type = 2 AND (encrypted_name IS NULL OR encrypted_name = ''))";
            $getPassagesToUpdateStmt = $this->db->prepare($getPassagesToUpdateQuery);
            $getPassagesToUpdateStmt->execute(['sector_id' => $id]);
            $passagesToUpdate = $getPassagesToUpdateStmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Supprimer les passages avec fk_type=2 et encrypted_name vide
            $deletePassagesQuery = "DELETE FROM ope_pass 
                                   WHERE fk_sector = :sector_id 
                                     AND fk_type = 2 
                                     AND (encrypted_name IS NULL OR encrypted_name = '')";
            $deletePassagesStmt = $this->db->prepare($deletePassagesQuery);
            $deletePassagesStmt->execute(['sector_id' => $id]);
            
            // Réaffecter les autres passages au secteur NULL
            $updatePassagesQuery = "UPDATE ope_pass 
                                   SET fk_sector = NULL 
                                   WHERE fk_sector = :sector_id 
                                     AND NOT (fk_type = 2 AND (encrypted_name IS NULL OR encrypted_name = ''))";
            $updatePassagesStmt = $this->db->prepare($updatePassagesQuery);
            $updatePassagesStmt->execute(['sector_id' => $id]);
            
            // Supprimer les affectations de membres
            $deleteMembersQuery = "DELETE FROM ope_users_sectors WHERE fk_sector = :sector_id";
            $deleteMembersStmt = $this->db->prepare($deleteMembersQuery);
            $deleteMembersStmt->execute(['sector_id' => $id]);
            
            // Supprimer les adresses associées au secteur
            $deleteAddressesQuery = "DELETE FROM sectors_adresses WHERE fk_sector = :sector_id";
            $deleteAddressesStmt = $this->db->prepare($deleteAddressesQuery);
            $deleteAddressesStmt->execute(['sector_id' => $id]);
            
            // Supprimer le secteur
            $deleteQuery = "DELETE FROM ope_sectors WHERE id = :id";
            $deleteStmt = $this->db->prepare($deleteQuery);
            $deleteStmt->execute(['id' => $id]);

            $this->db->commit();

            // Log de suppression du secteur (suppression physique = false)
            EventLogService::logSectorDeleted(
                (int)$id,
                (int)$operationId,
                false // suppression physique (DELETE)
            );

            // Déchiffrer les données sensibles des passages
            $passagesDecrypted = [];
            foreach ($passagesToUpdate 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']);
                
                $passagesDecrypted[] = $passage;
            }
            
            LogService::info('Secteur supprimé', [
                'sector_id' => $id,
                'libelle' => $sector['libelle'],
                'passages_deleted' => $passagesToDelete,
                'passages_reassigned' => count($passagesToUpdate),
                'user_id' => $_SESSION['user_id'] ?? null
            ]);
            
            Response::json([
                'status' => 'success',
                'message' => 'Secteur supprimé avec succès',
                'passages_deleted' => $passagesToDelete,
                'passages_reassigned' => count($passagesToUpdate),
                'passages_sector' => $passagesDecrypted
            ]);
            
        } catch (\Exception $e) {
            $this->db->rollBack();
            LogService::error('Erreur lors de la suppression du secteur', [
                'sector_id' => $id,
                'error' => $e->getMessage()
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la suppression du secteur'], 500);
        }
    }
    
    /**
     * GET /sectors/department-boundaries/status - Vérifier le statut des contours départementaux
     */
    public function departmentBoundariesStatus(): void
    {
        try {
            $status = $this->boundaryService->checkDepartmentContoursStatus();
            
            Response::json([
                'status' => 'success',
                'data' => $status
            ]);
            
        } catch (\Exception $e) {
            LogService::error('Erreur lors de la vérification des contours départementaux', [
                'error' => $e->getMessage()
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la vérification'], 500);
        }
    }
    
    /**
     * POST /sectors/check-boundaries - Vérifier si un secteur respecte les limites départementales
     */
    public function checkBoundaries(): void
    {
        try {
            $data = json_decode(file_get_contents('php://input'), true);
            
            if (!isset($data['sector']) || !isset($data['entity_id'])) {
                Response::json(['status' => 'error', 'message' => 'Données manquantes'], 400);
                return;
            }
            
            // Traiter les coordonnées
            $sector = $data['sector'];
            $points = explode('#', rtrim($sector, '#'));
            $coordinates = [];
            
            foreach ($points as $point) {
                if (!empty($point)) {
                    list($lat, $lng) = explode('/', $point);
                    $coordinates[] = [floatval($lat), floatval($lng)];
                }
            }
            
            // Récupérer le département de l'entité
            $deptQuery = "SELECT departement, nom FROM entites WHERE id = :entity_id";
            $deptStmt = $this->db->prepare($deptQuery);
            $deptStmt->execute(['entity_id' => $data['entity_id']]);
            $entity = $deptStmt->fetch();
            
            if (!$entity || !$entity['departement']) {
                Response::json(['status' => 'error', 'message' => 'Département de l\'entité non défini'], 400);
                return;
            }
            
            // Vérifier les limites
            $boundaryCheck = $this->boundaryService->checkSectorInDepartment($coordinates, $entity['departement']);
            
            // Récupérer aussi la liste de tous les départements touchés
            $intersectingDepts = $this->boundaryService->getDepartmentsForSector($coordinates);
            
            Response::json([
                'status' => 'success',
                'data' => [
                    'is_valid' => $boundaryCheck['is_contained'],
                    'entity_department' => $entity['departement'],
                    'message' => $boundaryCheck['message'],
                    'departments' => $intersectingDepts
                ]
            ]);
            
        } catch (\Exception $e) {
            LogService::error('Erreur lors de la vérification des limites', [
                'error' => $e->getMessage()
            ]);
            Response::json(['status' => 'error', 'message' => 'Erreur lors de la vérification'], 500);
        }
    }
    
    /**
     * Mettre à jour les passages affectés à un secteur lors de la modification du périmètre
     * VERSION OPTIMISÉE avec requêtes groupées
     * Retourne un tableau avec les compteurs détaillés
     */
    private function updatePassagesForSector($sectorId, $newSectorCoords): array
    {
        $counters = [
            'passages_orphaned' => 0,
            'passages_deleted' => 0,
            'passages_updated' => 0,
            'passages_created' => 0,
            'passages_kept' => 0
        ];

        try {
            // Récupérer l'opération et l'entité du secteur
            $sectorQuery = "SELECT o.id as operation_id, o.fk_entite, s.fk_operation
                           FROM ope_sectors s
                           JOIN operations o ON s.fk_operation = o.id
                           WHERE s.id = :sector_id";
            $sectorStmt = $this->db->prepare($sectorQuery);
            $sectorStmt->execute(['sector_id' => $sectorId]);
            $sectorInfo = $sectorStmt->fetch();

            if (!$sectorInfo) {
                return $counters;
            }

            $operationId = $sectorInfo['operation_id'];
            $entityId = $sectorInfo['fk_entite'];
            
            // Traiter les coordonnées pour créer le polygone
            $points = explode('#', rtrim($newSectorCoords, '#'));
            $coordinates = [];
            $polygonPoints = [];

            foreach ($points as $point) {
                if (!empty($point)) {
                    list($lat, $lng) = explode('/', $point);
                    $coordinates[] = [floatval($lat), floatval($lng)];
                    $polygonPoints[] = floatval($lng) . ' ' . floatval($lat); // longitude latitude pour SQL
                }
            }
            $polygonPoints[] = $polygonPoints[0]; // Fermer le polygone
            $polygonString = 'POLYGON((' . implode(',', $polygonPoints) . '))';
            
            // 1. VÉRIFICATION GÉOGRAPHIQUE DES PASSAGES EXISTANTS (OPTIMISÉE)
            // Utiliser une seule requête pour vérifier tous les passages
            $checkPassagesQuery = "
                SELECT
                    p.id,
                    p.gps_lat,
                    p.gps_lng,
                    p.fk_type,
                    p.encrypted_name,
                    ST_Contains(ST_GeomFromText(:polygon, 4326),
                               POINT(CAST(p.gps_lng AS DECIMAL(10,8)),
                                     CAST(p.gps_lat AS DECIMAL(10,8)))) as is_inside
                FROM ope_pass p
                WHERE p.fk_sector = :sector_id
                  AND p.gps_lat IS NOT NULL
                  AND p.gps_lng IS NOT NULL";

            $checkStmt = $this->db->prepare($checkPassagesQuery);
            $checkStmt->execute([
                'sector_id' => $sectorId,
                'polygon' => $polygonString
            ]);
            $existingPassages = $checkStmt->fetchAll();

            $passagesToDelete = [];
            $passagesToOrphan = [];

            foreach ($existingPassages as $passage) {
                if ($passage['is_inside'] == 0) {
                    // Le passage est hors du nouveau périmètre
                    if ($passage['fk_type'] == 2 && ($passage['encrypted_name'] === '' || $passage['encrypted_name'] === null)) {
                        // Passage non visité : à supprimer
                        $passagesToDelete[] = $passage['id'];
                        $counters['passages_deleted']++;
                    } else {
                        // Passage visité : à mettre en orphelin
                        $passagesToOrphan[] = $passage['id'];
                        $counters['passages_orphaned']++;
                    }
                } else {
                    $counters['passages_kept']++;
                }
            }

            // Supprimer les passages non visités en une seule requête
            if (!empty($passagesToDelete)) {
                $placeholders = str_repeat('?,', count($passagesToDelete) - 1) . '?';
                $deleteQuery = "DELETE FROM ope_pass WHERE id IN ($placeholders)";
                $deleteStmt = $this->db->prepare($deleteQuery);
                $deleteStmt->execute($passagesToDelete);
            }

            // Mettre en orphelin les passages visités en une seule requête
            if (!empty($passagesToOrphan)) {
                $placeholders = str_repeat('?,', count($passagesToOrphan) - 1) . '?';
                $orphanQuery = "UPDATE ope_pass SET fk_sector = NULL WHERE id IN ($placeholders)";
                $orphanStmt = $this->db->prepare($orphanQuery);
                $orphanStmt->execute($passagesToOrphan);
            }
            
            // 2. CRÉATION/MISE À JOUR DES PASSAGES POUR LES NOUVELLES ADRESSES (OPTIMISÉE)
            // Récupérer toutes les adresses du secteur depuis sectors_adresses (avec colonnes bâtiments)
            $addressesQuery = "SELECT
                fk_sector, fk_adresse, numero, rue, rue_bis, cp, ville, gps_lat, gps_lng,
                fk_batiment, fk_habitat, nb_niveau, nb_log, residence, alt_sol
                FROM sectors_adresses WHERE fk_sector = :sector_id";
            $addressesStmt = $this->db->prepare($addressesQuery);
            $addressesStmt->execute(['sector_id' => $sectorId]);
            $addresses = $addressesStmt->fetchAll();

            LogService::info('[updatePassagesForSector] Adresses dans sectors_adresses', [
                'sector_id' => $sectorId,
                'nb_addresses' => count($addresses)
            ]);

            // Récupérer le premier utilisateur affecté au secteur
            $userQuery = "SELECT fk_user FROM ope_users_sectors WHERE fk_sector = :sector_id LIMIT 1";
            $userStmt = $this->db->prepare($userQuery);
            $userStmt->execute(['sector_id' => $sectorId]);
            $firstUser = $userStmt->fetch();
            $firstUserId = $firstUser ? $firstUser['fk_user'] : null;

            if ($firstUserId && !empty($addresses)) {
                LogService::info('[updatePassagesForSector] Traitement des passages', [
                    'user_id' => $firstUserId,
                    'nb_addresses' => count($addresses)
                ]);

                // Récupérer TOUS les passages existants pour cette opération en UNE requête
                $existingQuery = "
                    SELECT id, fk_adresse, numero, rue, rue_bis, ville, residence, appt, fk_habitat,
                           fk_type, encrypted_name, created_at
                    FROM ope_pass
                    WHERE fk_operation = :operation_id";

                $existingStmt = $this->db->prepare($existingQuery);
                $existingStmt->execute(['operation_id' => $operationId]);
                $existingPassages = $existingStmt->fetchAll();

                // Indexer les passages existants par clé : numero|rue|rue_bis|ville
                $passagesByAddress = [];
                foreach ($existingPassages as $p) {
                    $addressKey = $p['numero'] . '|' . $p['rue'] . '|' . ($p['rue_bis'] ?? '') . '|' . $p['ville'];
                    if (!isset($passagesByAddress[$addressKey])) {
                        $passagesByAddress[$addressKey] = [];
                    }
                    $passagesByAddress[$addressKey][] = $p;
                }

                // Traiter chaque adresse du secteur
                $toInsert = [];
                $toUpdate = [];
                $toDelete = [];

                foreach ($addresses as $address) {
                    $addressKey = $address['numero'] . '|' . $address['rue'] . '|' . ($address['rue_bis'] ?? '') . '|' . $address['ville'];
                    $existingAtAddress = $passagesByAddress[$addressKey] ?? [];
                    $nbExisting = count($existingAtAddress);

                    $fkHabitat = $address['fk_habitat'] ?? 1;
                    $nbLog = ($fkHabitat == 2 && isset($address['nb_log'])) ? (int)$address['nb_log'] : 1;
                    $residence = $address['residence'] ?? '';

                    // IMPORTANT : Uniformisation GPS pour les immeubles
                    // Tous les passages d'une même adresse doivent partager les mêmes coordonnées GPS
                    // Issues de sectors_adresses (gps_lat, gps_lng)
                    $gpsLat = $address['gps_lat'];
                    $gpsLng = $address['gps_lng'];

                    // CAS 1 : Maison individuelle (fk_habitat=1)
                    if ($fkHabitat == 1) {
                        if ($nbExisting == 0) {
                            // INSERT 1 passage
                            $toInsert[] = [
                                'address' => $address,
                                'residence' => '',
                                'appt' => '',
                                'fk_habitat' => 1
                            ];
                        } else {
                            // UPDATE le premier passage avec fk_habitat=1
                            $toUpdate[] = [
                                'id' => $existingAtAddress[0]['id'],
                                'fk_habitat' => 1,
                                'residence' => '',
                                'gps_lat' => $gpsLat,
                                'gps_lng' => $gpsLng
                            ];
                            // Les autres passages (si >1) ne sont PAS touchés
                        }
                    }
                    // CAS 2 : Immeuble (fk_habitat=2)
                    else if ($fkHabitat == 2) {
                        // UPDATE TOUS les passages existants avec fk_habitat=2, residence et GPS
                        foreach ($existingAtAddress as $existing) {
                            $updates = [
                                'id' => $existing['id'],
                                'fk_habitat' => 2,
                                'gps_lat' => $gpsLat,
                                'gps_lng' => $gpsLng
                            ];
                            // Update residence seulement si non vide
                            if (!empty($residence)) {
                                $updates['residence'] = $residence;
                            }
                            $toUpdate[] = $updates;
                        }

                        // Si moins de nb_log passages : INSERT les manquants
                        if ($nbExisting < $nbLog) {
                            $nbToInsert = $nbLog - $nbExisting;
                            for ($i = 0; $i < $nbToInsert; $i++) {
                                $toInsert[] = [
                                    'address' => $address,
                                    'residence' => $residence,
                                    'appt' => '', // Pas de numéro d'appt prédéfini
                                    'fk_habitat' => 2
                                ];
                            }
                        }
                        // Si plus de nb_log passages : DELETE les non visités en trop
                        else if ($nbExisting > $nbLog) {
                            $nbToDelete = $nbExisting - $nbLog;
                            // Trier les passages par created_at ASC (les plus anciens d'abord)
                            usort($existingAtAddress, function($a, $b) {
                                return strtotime($a['created_at']) - strtotime($b['created_at']);
                            });

                            $deleted = 0;
                            foreach ($existingAtAddress as $existing) {
                                if ($deleted >= $nbToDelete) break;
                                // Supprimer seulement si fk_type=2 ET encrypted_name vide
                                if ($existing['fk_type'] == 2 && ($existing['encrypted_name'] === '' || $existing['encrypted_name'] === null)) {
                                    $toDelete[] = $existing['id'];
                                    $deleted++;
                                }
                            }
                        }
                    }
                }

                // INSERT MULTIPLE en une seule requête
                if (!empty($toInsert)) {
                    $values = [];
                    $insertParams = [];
                    $paramIndex = 0;

                    foreach ($toInsert as $item) {
                        $addr = $item['address'];
                        $values[] = "(:op$paramIndex, :sect$paramIndex, :usr$paramIndex, :addr$paramIndex,
                                     :num$paramIndex, :rue$paramIndex, :bis$paramIndex, :ville$paramIndex,
                                     :res$paramIndex, :appt$paramIndex, :habitat$paramIndex,
                                     :lat$paramIndex, :lng$paramIndex, 2, 0, '', NOW(), :creat$paramIndex, 1)";

                        $insertParams["op$paramIndex"] = $operationId;
                        $insertParams["sect$paramIndex"] = $sectorId;
                        $insertParams["usr$paramIndex"] = $firstUserId;
                        $insertParams["addr$paramIndex"] = $addr['fk_adresse'] ?? null;
                        $insertParams["num$paramIndex"] = $addr['numero'];
                        $insertParams["rue$paramIndex"] = $addr['rue'];
                        $insertParams["bis$paramIndex"] = $addr['rue_bis'] ?? '';
                        $insertParams["ville$paramIndex"] = $addr['ville'];
                        $insertParams["res$paramIndex"] = $item['residence'];
                        $insertParams["appt$paramIndex"] = $item['appt'];
                        $insertParams["habitat$paramIndex"] = $item['fk_habitat'];
                        $insertParams["lat$paramIndex"] = $addr['gps_lat'];
                        $insertParams["lng$paramIndex"] = $addr['gps_lng'];
                        $insertParams["creat$paramIndex"] = $_SESSION['user_id'] ?? null;

                        $paramIndex++;
                    }

                    $insertQuery = "INSERT INTO ope_pass
                        (fk_operation, fk_sector, fk_user, fk_adresse, numero, rue, rue_bis,
                         ville, residence, appt, fk_habitat, gps_lat, gps_lng, fk_type, nb_passages, encrypted_name, created_at, fk_user_creat, chk_active)
                        VALUES " . implode(',', $values);

                    try {
                        $insertStmt = $this->db->prepare($insertQuery);
                        $insertStmt->execute($insertParams);
                        $counters['passages_created'] = count($toInsert);
                    } catch (\Exception $e) {
                        LogService::error('Erreur lors de l\'insertion multiple des passages', [
                            'sector_id' => $sectorId,
                            'error' => $e->getMessage()
                        ]);
                    }
                }

                // UPDATE MULTIPLE avec CASE WHEN (inclut GPS pour uniformisation)
                if (!empty($toUpdate)) {
                    $updateIds = array_column($toUpdate, 'id');
                    $placeholders = str_repeat('?,', count($updateIds) - 1) . '?';

                    $caseWhenHabitat = [];
                    $caseWhenResidence = [];
                    $caseWhenGpsLat = [];
                    $caseWhenGpsLng = [];
                    $updateParams = [];

                    foreach ($toUpdate as $upd) {
                        // fk_habitat est toujours présent
                        $caseWhenHabitat[] = "WHEN id = ? THEN ?";
                        $updateParams[] = $upd['id'];
                        $updateParams[] = $upd['fk_habitat'];

                        // GPS : toujours présent maintenant (uniformisation)
                        if (isset($upd['gps_lat']) && isset($upd['gps_lng'])) {
                            $caseWhenGpsLat[] = "WHEN id = ? THEN ?";
                            $updateParams[] = $upd['id'];
                            $updateParams[] = $upd['gps_lat'];

                            $caseWhenGpsLng[] = "WHEN id = ? THEN ?";
                            $updateParams[] = $upd['id'];
                            $updateParams[] = $upd['gps_lng'];
                        }

                        // residence est optionnel
                        if (isset($upd['residence'])) {
                            $caseWhenResidence[] = "WHEN id = ? THEN ?";
                            $updateParams[] = $upd['id'];
                            $updateParams[] = $upd['residence'];
                        }
                    }

                    $setClause = ["fk_habitat = CASE " . implode(' ', $caseWhenHabitat) . " ELSE fk_habitat END"];
                    if (!empty($caseWhenGpsLat)) {
                        $setClause[] = "gps_lat = CASE " . implode(' ', $caseWhenGpsLat) . " ELSE gps_lat END";
                    }
                    if (!empty($caseWhenGpsLng)) {
                        $setClause[] = "gps_lng = CASE " . implode(' ', $caseWhenGpsLng) . " ELSE gps_lng END";
                    }
                    if (!empty($caseWhenResidence)) {
                        $setClause[] = "residence = CASE " . implode(' ', $caseWhenResidence) . " ELSE residence END";
                    }

                    $updateQuery = "UPDATE ope_pass
                                   SET " . implode(', ', $setClause) . "
                                   WHERE id IN ($placeholders)";

                    try {
                        $updateStmt = $this->db->prepare($updateQuery);
                        $updateStmt->execute(array_merge($updateParams, $updateIds));
                        $counters['passages_updated'] = count($toUpdate);

                        // Log pour vérifier l'uniformisation GPS (surtout pour immeubles)
                        LogService::info('[updatePassagesForSector] Passages mis à jour avec GPS uniformisés', [
                            'nb_updated' => count($toUpdate),
                            'sector_id' => $sectorId
                        ]);
                    } catch (\Exception $e) {
                        LogService::error('Erreur lors de la mise à jour multiple des passages', [
                            'sector_id' => $sectorId,
                            'error' => $e->getMessage()
                        ]);
                    }
                }

                // DELETE MULTIPLE en une seule requête
                if (!empty($toDelete)) {
                    $placeholders = str_repeat('?,', count($toDelete) - 1) . '?';
                    $deleteQuery = "DELETE FROM ope_pass WHERE id IN ($placeholders)";

                    try {
                        $deleteStmt = $this->db->prepare($deleteQuery);
                        $deleteStmt->execute($toDelete);
                        $counters['passages_deleted'] += count($toDelete);
                    } catch (\Exception $e) {
                        LogService::error('Erreur lors de la suppression multiple des passages', [
                            'sector_id' => $sectorId,
                            'error' => $e->getMessage()
                        ]);
                    }
                }

            } else {
                LogService::warning('[updatePassagesForSector] Pas de création de passages', [
                    'reason' => !$firstUserId ? 'Pas d\'utilisateur affecté' : 'Pas d\'adresses',
                    'first_user_id' => $firstUserId,
                    'nb_addresses' => count($addresses)
                ]);
            }
            
            
            // Retourner les compteurs détaillés
            LogService::info('[updatePassagesForSector] Fin traitement', [
                'sector_id' => $sectorId,
                'counters' => $counters
            ]);
            return $counters;
            
        } catch (\Exception $e) {
            LogService::error('Erreur lors de la mise à jour des passages', [
                'sector_id' => $sectorId,
                'error' => $e->getMessage()
            ]);
            return [
                'passages_orphaned' => 0,
                'passages_deleted' => 0,
                'passages_updated' => 0,
                'passages_created' => 0,
                'passages_kept' => 0
            ];
        }
    }
}