<?php

declare(strict_types=1);

namespace App\Services;

use Database;

use PDO;
use PDOException;
use RuntimeException;

class DepartmentBoundaryService {
    private PDO $db;
    
    public function __construct() {
        $this->db = \Database::getInstance();
    }
    
    /**
     * Vérifie si un polygone (secteur) est entièrement contenu dans un département
     * 
     * @param array $sectorCoordinates Coordonnées du secteur [[lat, lng], ...]
     * @param string $departmentCode Code du département (22, 29, etc.)
     * @return array ['is_contained' => bool, 'message' => string, 'intersecting_departments' => array]
     */
    public function checkSectorInDepartment(array $sectorCoordinates, string $departmentCode): array {
        if (count($sectorCoordinates) < 3) {
            return [
                'is_contained' => false,
                'message' => 'Un secteur doit avoir au moins 3 points',
                'intersecting_departments' => []
            ];
        }
        
        // Créer le polygone du secteur
        $polygonPoints = [];
        foreach ($sectorCoordinates as $coord) {
            if (!isset($coord[0]) || !isset($coord[1])) {
                return [
                    'is_contained' => false,
                    'message' => 'Coordonnées invalides',
                    'intersecting_departments' => []
                ];
            }
            $polygonPoints[] = $coord[1] . ' ' . $coord[0]; // longitude latitude
        }
        
        // Fermer le polygone
        $polygonPoints[] = $polygonPoints[0];
        $sectorPolygon = 'POLYGON((' . implode(',', $polygonPoints) . '))';
        
        try {
            // 1. Vérifier si le secteur est entièrement dans le département cible
            $sql = "SELECT 
                        code_dept,
                        nom_dept,
                        ST_Contains(contour, ST_GeomFromText(:sector_polygon, 4326)) as is_contained,
                        ST_Intersects(contour, ST_GeomFromText(:sector_polygon, 4326)) as intersects
                    FROM x_departements
                    WHERE code = :dept_code
                      AND contour IS NOT NULL";
            
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                'sector_polygon' => $sectorPolygon,
                'dept_code' => $departmentCode
            ]);
            
            $targetDept = $stmt->fetch();
            
            if (!$targetDept) {
                return [
                    'is_contained' => false,
                    'message' => "Le département $departmentCode n'a pas de contour défini",
                    'intersecting_departments' => []
                ];
            }
            
            // 2. Si le secteur n'est pas entièrement contenu, trouver tous les départements qu'il touche
            if (!$targetDept['is_contained']) {
                $sql = "SELECT 
                            code_dept,
                            nom_dept,
                            ST_Area(ST_Intersection(contour, ST_GeomFromText(:sector_polygon1, 4326))) / 
                            ST_Area(ST_GeomFromText(:sector_polygon2, 4326)) * 100 as percentage_overlap
                        FROM x_departements_contours
                        WHERE ST_Intersects(contour, ST_GeomFromText(:sector_polygon3, 4326))
                        ORDER BY percentage_overlap DESC";
                
                $stmt = $this->db->prepare($sql);
                $stmt->execute([
                    'sector_polygon1' => $sectorPolygon,
                    'sector_polygon2' => $sectorPolygon,
                    'sector_polygon3' => $sectorPolygon
                ]);
                
                $intersectingDepts = $stmt->fetchAll(\PDO::FETCH_ASSOC);
                
                // Formater le message
                $deptsList = array_map(function($d) {
                    return sprintf("%s (%s) : %.1f%%", 
                        $d['nom_dept'], 
                        $d['code_dept'], 
                        $d['percentage_overlap']
                    );
                }, $intersectingDepts);
                
                return [
                    'is_contained' => false,
                    'message' => "Le secteur déborde du département {$targetDept['nom_dept']}. Il est à cheval sur : " . implode(', ', $deptsList),
                    'intersecting_departments' => $intersectingDepts
                ];
            }
            
            return [
                'is_contained' => true,
                'message' => "Le secteur est entièrement contenu dans le département {$targetDept['nom_dept']}",
                'intersecting_departments' => [$targetDept]
            ];
            
        } catch (\PDOException $e) {
            throw new RuntimeException("Erreur lors de la vérification des limites départementales : " . $e->getMessage());
        }
    }
    
    /**
     * Récupère les départements qui intersectent avec un secteur
     * 
     * @param array $sectorCoordinates Coordonnées du secteur [[lat, lng], ...]
     * @return array Liste des départements avec leur pourcentage de recouvrement
     */
    public function getDepartmentsForSector(array $sectorCoordinates): array {
        if (count($sectorCoordinates) < 3) {
            return [];
        }
        
        // Créer le polygone du secteur
        $polygonPoints = [];
        foreach ($sectorCoordinates as $coord) {
            $polygonPoints[] = $coord[1] . ' ' . $coord[0]; // longitude latitude
        }
        $polygonPoints[] = $polygonPoints[0];
        $sectorPolygon = 'POLYGON((' . implode(',', $polygonPoints) . '))';
        
        try {
            $sql = "SELECT 
                        code_dept,
                        nom_dept,
                        ST_Area(ST_Intersection(contour, ST_GeomFromText(:sector_polygon1, 4326))) / 
                        ST_Area(ST_GeomFromText(:sector_polygon2, 4326)) * 100 as percentage_overlap
                    FROM x_departements_contours
                    WHERE ST_Intersects(contour, ST_GeomFromText(:sector_polygon3, 4326))
                    ORDER BY percentage_overlap DESC";
            
            $stmt = $this->db->prepare($sql);
            $stmt->execute([
                'sector_polygon1' => $sectorPolygon,
                'sector_polygon2' => $sectorPolygon,
                'sector_polygon3' => $sectorPolygon
            ]);
            
            return $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
        } catch (\PDOException $e) {
            throw new RuntimeException("Erreur lors de la recherche des départements : " . $e->getMessage());
        }
    }
    
    /**
     * Vérifie si les contours des départements sont chargés
     * 
     * @return array ['loaded' => bool, 'count' => int, 'missing' => array]
     */
    public function checkDepartmentContoursStatus(): array {
        try {
            // Compter les départements avec contours
            $sql = "SELECT COUNT(*) as count FROM x_departements_contours";
            $stmt = $this->db->query($sql);
            $count = $stmt->fetch()['count'];
            
            // Récupérer la liste des départements utilisés dans les entités
            $sql = "SELECT DISTINCT departement 
                    FROM entites 
                    WHERE departement IS NOT NULL 
                    ORDER BY departement";
            $stmt = $this->db->query($sql);
            $usedDepts = $stmt->fetchAll(\PDO::FETCH_COLUMN);
            
            // Vérifier lesquels ont des contours
            $sql = "SELECT code_dept FROM x_departements_contours WHERE code_dept IN ('" . implode("','", $usedDepts) . "')";
            $stmt = $this->db->query($sql);
            $loadedDepts = $stmt->fetchAll(\PDO::FETCH_COLUMN);
            
            $missingDepts = array_diff($usedDepts, $loadedDepts);
            
            return [
                'loaded' => count($missingDepts) === 0,
                'count' => $count,
                'total_used' => count($usedDepts),
                'missing' => array_values($missingDepts)
            ];
            
        } catch (\PDOException $e) {
            return [
                'loaded' => false,
                'count' => 0,
                'total_used' => 0,
                'missing' => []
            ];
        }
    }
    
    /**
     * Récupère les départements limitrophes d'un département donné
     * 
     * @param string $departmentCode Code du département
     * @return array Array des codes départements limitrophes
     */
    public function getAdjacentDepartments(string $departmentCode): array {
        try {
            $sql = "SELECT dept_limitrophes FROM x_departements WHERE code = :dept_code AND chk_active = 1";
            $stmt = $this->db->prepare($sql);
            $stmt->execute(['dept_code' => $departmentCode]);
            
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$result || empty($result['dept_limitrophes'])) {
                return [];
            }
            
            // Convertir la chaîne CSV en array
            return array_map('trim', explode(',', $result['dept_limitrophes']));
            
        } catch (PDOException $e) {
            error_log("Erreur lors de la récupération des départements limitrophes : " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Récupère les départements à prioriser pour la recherche
     * (département de l'entité + ses limitrophes)
     * 
     * @param string $entityDepartment Code du département de l'entité
     * @return array Array des codes départements à prioriser
     */
    public function getPriorityDepartments(string $entityDepartment): array {
        $priorityDepts = [$entityDepartment]; // Commencer par le département de l'entité
        
        // Ajouter les départements limitrophes
        $adjacentDepts = $this->getAdjacentDepartments($entityDepartment);
        $priorityDepts = array_merge($priorityDepts, $adjacentDepts);
        
        // Retourner sans doublons
        return array_unique($priorityDepts);
    }
}