<?php

declare(strict_types=1);

namespace App\Services;

use PDO;
use PDOException;
use AppConfig;

require_once __DIR__ . '/../Config/AppConfig.php';

/**
 * Service de gestion des bâtiments
 *
 * Ce service enrichit les adresses trouvées par AddressService avec les métadonnées
 * des bâtiments depuis la base de données 'batiments'.
 *
 * Fonctionnalités :
 * - Enrichissement des adresses avec données bâtiment (nb_log, residence, etc.)
 * - Identification automatique des immeubles (fk_habitat=2)
 * - Gestion des adresses sans bâtiment (maisons individuelles, fk_habitat=1)
 */
class BuildingService
{
    private ?PDO $dbBuildings = null;
    private bool $connected = false;

    public function __construct()
    {
        $this->initConnection();
    }

    /**
     * Initialise la connexion à la base de données batiments
     */
    private function initConnection(): void
    {
        try {
            $config = AppConfig::getInstance()->getBuildingsDatabaseConfig();

            $dsn = sprintf(
                'mysql:host=%s;dbname=%s;charset=utf8mb4',
                $config['host'],
                $config['name']
            );

            $this->dbBuildings = new PDO(
                $dsn,
                $config['username'],
                $config['password'],
                [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                    PDO::ATTR_EMULATE_PREPARES => false,
                ]
            );

            $this->connected = true;

        } catch (PDOException $e) {
            error_log("Erreur de connexion à la base batiments : " . $e->getMessage());
            $this->connected = false;
        }
    }

    /**
     * Vérifie si la connexion à la base batiments est active
     *
     * @return bool True si connecté
     */
    public function isConnected(): bool
    {
        return $this->connected;
    }

    /**
     * Enrichit une liste d'adresses avec les données bâtiment
     *
     * Pour chaque adresse trouvée par AddressService, cette méthode cherche
     * si un bâtiment existe dans bat{dept} avec le lien cle_interop_adr.
     *
     * @param array $addresses Liste d'adresses depuis AddressService
     *                         Format attendu : ['id' => 'cp22.12345', 'departement' => '22', ...]
     * @return array Adresses enrichies avec :
     *               - fk_batiment : batiment_groupe_id (ou null)
     *               - fk_habitat : 1=individuel, 2=collectif
     *               - nb_niveau : Nombre d'étages (ou null)
     *               - nb_log : Nombre de logements (ou null)
     *               - residence : Nom de la copropriété (ou '')
     *               - alt_sol : Altitude sol (ou null)
     */
    public function enrichAddresses(array $addresses): array
    {
        // Si pas de connexion, retourner les adresses sans enrichissement
        if (!$this->isConnected() || empty($addresses)) {
            // Ajouter fk_habitat=1 par défaut (maison individuelle)
            foreach ($addresses as &$address) {
                $address['fk_batiment'] = null;
                $address['fk_habitat'] = 1;
                $address['nb_niveau'] = null;
                $address['nb_log'] = null;
                $address['residence'] = '';
                $address['alt_sol'] = null;
            }
            return $addresses;
        }

        try {
            // Grouper les adresses par département
            $addressesByDept = [];
            foreach ($addresses as $address) {
                $dept = $this->extractDepartmentFromAddress($address);
                if ($dept) {
                    $addressesByDept[$dept][] = $address;
                }
            }

            // Enrichir les adresses département par département
            $enrichedAddresses = [];

            foreach ($addressesByDept as $dept => $deptAddresses) {
                $enrichedDept = $this->enrichAddressesByDepartment((string)$dept, $deptAddresses);
                $enrichedAddresses = array_merge($enrichedAddresses, $enrichedDept);
            }

            return $enrichedAddresses;

        } catch (PDOException $e) {
            error_log("Erreur lors de l'enrichissement des adresses avec batiments : " . $e->getMessage());

            // En cas d'erreur, retourner les adresses avec valeurs par défaut
            foreach ($addresses as &$address) {
                $address['fk_batiment'] = null;
                $address['fk_habitat'] = 1;
                $address['nb_niveau'] = null;
                $address['nb_log'] = null;
                $address['residence'] = '';
                $address['alt_sol'] = null;
            }
            return $addresses;
        }
    }

    /**
     * Enrichit les adresses d'un département spécifique
     *
     * @param string $dept Code du département (ex: '22', '35')
     * @param array $addresses Liste des adresses du département
     * @return array Adresses enrichies
     */
    private function enrichAddressesByDepartment(string $dept, array $addresses): array
    {
        // Vérifier que la table bat{dept} existe
        if (!$this->tableExists("bat{$dept}")) {
            // Table inexistante, retourner avec valeurs par défaut
            foreach ($addresses as &$address) {
                $address['fk_batiment'] = null;
                $address['fk_habitat'] = 1;
                $address['nb_niveau'] = null;
                $address['nb_log'] = null;
                $address['residence'] = '';
                $address['alt_sol'] = null;
            }
            return $addresses;
        }

        // Créer un mapping address_id => address pour retrouver rapidement
        $addressMap = [];
        $addressIds = [];

        foreach ($addresses as $address) {
            // Extraire l'ID BAN (partie après le point)
            $addressId = $this->extractAddressId($address['id']);
            if ($addressId) {
                $addressIds[] = $addressId;
                $addressMap[$addressId] = $address;
            }
        }

        if (empty($addressIds)) {
            // Pas d'IDs valides, retourner avec valeurs par défaut
            foreach ($addresses as &$address) {
                $address['fk_batiment'] = null;
                $address['fk_habitat'] = 1;
                $address['nb_niveau'] = null;
                $address['nb_log'] = null;
                $address['residence'] = '';
                $address['alt_sol'] = null;
            }
            return $addresses;
        }

        // Requête pour récupérer les bâtiments
        $placeholders = str_repeat('?,', count($addressIds) - 1) . '?';
        $query = "
            SELECT
                b.cle_interop_adr,
                b.batiment_groupe_id,
                b.nb_niveau,
                b.nb_log,
                b.residence,
                b.altitude_sol_mean
            FROM bat{$dept} b
            WHERE b.cle_interop_adr IN ($placeholders)
        ";

        $stmt = $this->dbBuildings->prepare($query);
        $stmt->execute($addressIds);
        $buildings = $stmt->fetchAll();

        // Créer un mapping cle_interop_adr => building
        $buildingMap = [];
        foreach ($buildings as $building) {
            $buildingMap[$building['cle_interop_adr']] = $building;
        }

        // Enrichir les adresses
        $enrichedAddresses = [];
        foreach ($addresses as $address) {
            $addressId = $this->extractAddressId($address['id']);

            if ($addressId && isset($buildingMap[$addressId])) {
                // Bâtiment trouvé : enrichir avec ses données
                $building = $buildingMap[$addressId];
                $address['fk_batiment'] = $building['batiment_groupe_id'];
                $address['fk_habitat'] = 2; // Collectif
                $address['nb_niveau'] = $building['nb_niveau'];
                $address['nb_log'] = $building['nb_log'];
                $address['residence'] = $building['residence'] ?? '';
                $address['alt_sol'] = $building['altitude_sol_mean'];
            } else {
                // Pas de bâtiment : maison individuelle
                $address['fk_batiment'] = null;
                $address['fk_habitat'] = 1; // Individuel
                $address['nb_niveau'] = null;
                $address['nb_log'] = null;
                $address['residence'] = '';
                $address['alt_sol'] = null;
            }

            $enrichedAddresses[] = $address;
        }

        return $enrichedAddresses;
    }

    /**
     * Extrait le code département depuis une adresse
     *
     * @param array $address Adresse depuis AddressService
     * @return string|null Code département (ex: '22', '35')
     */
    private function extractDepartmentFromAddress(array $address): ?string
    {
        // Méthode 1 : Clé 'departement' directement
        if (isset($address['departement'])) {
            return $address['departement'];
        }

        // Méthode 2 : Depuis code_postal
        if (isset($address['code_postal'])) {
            $cp = $address['code_postal'];
            if (strlen($cp) === 4) {
                return '0' . substr($cp, 0, 1);
            }
            return substr($cp, 0, 2);
        }

        // Méthode 3 : Depuis l'ID (format cp22.12345)
        if (isset($address['id']) && strpos($address['id'], 'cp') === 0) {
            preg_match('/^cp(\d{2})\./', $address['id'], $matches);
            if (isset($matches[1])) {
                return $matches[1];
            }
        }

        return null;
    }

    /**
     * Extrait l'ID BAN depuis l'ID complet (ex: cp22.12345 → 12345)
     *
     * @param string $fullId ID complet de l'adresse
     * @return string|null ID BAN
     */
    private function extractAddressId(string $fullId): ?string
    {
        if (strpos($fullId, '.') !== false) {
            $parts = explode('.', $fullId);
            return $parts[1] ?? null;
        }
        return $fullId;
    }

    /**
     * Vérifie si une table existe dans la base batiments
     *
     * @param string $tableName Nom de la table (ex: 'bat22')
     * @return bool True si la table existe
     */
    private function tableExists(string $tableName): bool
    {
        try {
            // SHOW TABLES LIKE ne supporte pas les placeholders PDO
            // On échappe manuellement le nom de table (alphanumérique uniquement)
            if (!preg_match('/^[a-zA-Z0-9_]+$/', $tableName)) {
                error_log("Nom de table invalide : {$tableName}");
                return false;
            }

            $stmt = $this->dbBuildings->query("SHOW TABLES LIKE '{$tableName}'");
            return $stmt && $stmt->rowCount() > 0;
        } catch (PDOException $e) {
            error_log("Erreur lors de la vérification de la table {$tableName} : " . $e->getMessage());
            return false;
        }
    }
}
