<?php
// app/model/service/ModuleService.php

require_once __DIR__ . '/../core/AbstractPdo.php';

class ModuleService extends AbstractPdo {
    protected $table = 'module';
    protected $primaryKey = 'id';

    public function __construct() {
        parent::__construct();
    }

    /**
     * Rechercher des modules avec critères
     */
    public function search($searchData, $page = 1, $perPage = 10) {
        $filters = [];

        // Filtres sur les champs de module
        $searchFields = ['id', 'libelle', 'code', 'description'];
        foreach ($searchFields as $field) {
            if (!empty($searchData[$field])) {
                $filters[$field] = [
                    'value' => $searchData[$field],
                    'like' => true
                ];
            }
        }

        // Filtre par filière_id
        if (!empty($searchData['filiere_id'])) {
            $filters['filiere_id'] = [
                'value' => $searchData['filiere_id'],
                'like' => false
            ];
        }

        // Filtre par semestre_id
        if (!empty($searchData['semestre_id'])) {
            $filters['semestre_id'] = [
                'value' => $searchData['semestre_id'],
                'like' => false
            ];
        }

        // Filtre par année universitaire
        if (!empty($searchData['annee_universitaire'])) {
            $filters['annee_universitaire'] = [
                'value' => $searchData['annee_universitaire'],
                'like' => false
            ];
        }

        $offset = ($page - 1) * $perPage;
        $modules = $this->findWithRelations($filters, $perPage, $offset);
        $total = $this->countWithRelations($filters);

        return [
            'data' => $modules,
            'total' => $total,
            'page' => $page,
            'perPage' => $perPage,
            'totalPages' => ceil($total / $perPage)
        ];
    }

    /**
     * Find modules with all relations (semestre, filiere)
     */
    public function findWithRelations($filters = [], $limit = null, $offset = null) {
        $sql = "SELECT m.*,
                       s.libelle as semestre_libelle,
                       s.code as semestre_code,
                       s.annee_universitaire as semestre_annee,
                       f.libelle as filiere_libelle,
                       f.code as filiere_code
                FROM module m
                LEFT JOIN semestre s ON m.semestre_id = s.id
                LEFT JOIN filiere f ON m.filiere_id = f.id";

        $where = [];
        $params = [];

        foreach ($filters as $key => $filter) {
            $field = "m.{$key}";

            if (isset($filter['like']) && $filter['like'] && !empty($filter['value'])) {
                $paramKey = str_replace('.', '_', $key);
                $where[] = "{$field} LIKE :{$paramKey}";
                $params[":{$paramKey}"] = '%' . $filter['value'] . '%';
            } elseif (!empty($filter['value'])) {
                $paramKey = str_replace('.', '_', $key);
                $where[] = "{$field} = :{$paramKey}";
                $params[":{$paramKey}"] = $filter['value'];
            }
        }

        if (!empty($where)) {
            $sql .= " WHERE " . implode(' AND ', $where);
        }

        $sql .= " ORDER BY f.libelle ASC, m.libelle ASC";

        if ($limit !== null) {
            $sql .= " LIMIT " . (int)$limit;
            if ($offset !== null) {
                $sql .= " OFFSET " . (int)$offset;
            }
        }

        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll();
    }

    /**
     * Count modules with relations
     */
    public function countWithRelations($filters = []) {
        $sql = "SELECT COUNT(*) as count
                FROM module m
                LEFT JOIN semestre s ON m.semestre_id = s.id
                LEFT JOIN filiere f ON m.filiere_id = f.id";

        $where = [];
        $params = [];

        foreach ($filters as $key => $filter) {
            $field = "m.{$key}";

            if (isset($filter['like']) && $filter['like'] && !empty($filter['value'])) {
                $paramKey = str_replace('.', '_', $key);
                $where[] = "{$field} LIKE :{$paramKey}";
                $params[":{$paramKey}"] = '%' . $filter['value'] . '%';
            } elseif (!empty($filter['value'])) {
                $paramKey = str_replace('.', '_', $key);
                $where[] = "{$field} = :{$paramKey}";
                $params[":{$paramKey}"] = $filter['value'];
            }
        }

        if (!empty($where)) {
            $sql .= " WHERE " . implode(' AND ', $where);
        }

        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        $result = $stmt->fetch();
        return (int)$result['count'];
    }

    /**
     * Obtenir un module par ID avec toutes ses relations
     */
    public function getModule($id) {
        $sql = "SELECT m.*,
                       s.libelle as semestre_libelle,
                       s.code as semestre_code,
                       s.annee_universitaire as semestre_annee,
                       f.libelle as filiere_libelle,
                       f.code as filiere_code
                FROM module m
                LEFT JOIN semestre s ON m.semestre_id = s.id
                LEFT JOIN filiere f ON m.filiere_id = f.id
                WHERE m.id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        return $stmt->fetch();
    }

    /**
     * Obtenir un module par code
     */
    public function getModuleByCode($code) {
        return $this->findOneBy(['code' => $code]);
    }

    /**
     * Obtenir les modules par semestre
     */
    public function getModulesBySemestre($semestreId) {
        return $this->findAllBy(['semestre_id' => $semestreId], 'libelle ASC');
    }

    /**
     * Obtenir les modules par filière
     */
    public function getModulesByFiliere($filiereId) {
        return $this->findAllBy(['filiere_id' => $filiereId], 'libelle ASC');
    }

    /**
     * Obtenir les modules par année universitaire
     */
    public function getModulesByAnnee($annee) {
        $sql = "SELECT m.*,
                       s.libelle as semestre_libelle,
                       s.code as semestre_code,
                       f.libelle as filiere_libelle
                FROM module m
                JOIN semestre s ON m.semestre_id = s.id
                JOIN filiere f ON m.filiere_id = f.id
                WHERE m.annee_universitaire = :annee
                ORDER BY f.libelle ASC, m.libelle ASC";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':annee' => $annee]);
        return $stmt->fetchAll();
    }

    /**
     * Créer un nouveau module
     */
    public function createModule($data) {
        // Valider les données
        if (empty($data['libelle'])) {
            throw new Exception("Le libellé est obligatoire");
        }
        if (empty($data['code'])) {
            throw new Exception("Le code est obligatoire");
        }
        if (empty($data['filiere_id'])) {
            throw new Exception("La filière est obligatoire");
        }
        if (empty($data['semestre_id'])) {
            throw new Exception("Le semestre est obligatoire");
        }
        if (empty($data['annee_universitaire'])) {
            throw new Exception("L'année universitaire est obligatoire");
        }

        // Vérifier que le semestre existe
        $this->checkSemestreExists($data['semestre_id']);

        // Vérifier que la filière existe
        $this->checkFiliereExists($data['filiere_id']);

        // Vérifier les doublons
        $this->checkDuplicates($data);

        // Préparer les données avec les bons noms de colonnes
        $insertData = [
            'libelle' => $data['libelle'],
            'code' => $data['code'],
            'description' => $data['description'] ?? null,
            'filiere_id' => $data['filiere_id'],
            'charge_horaire_cours' => $data['charge_horaire_cours'] ?? 0,
            'charge_horaire_tp' => $data['charge_horaire_tp'] ?? 0,
            'charge_horaire_td' => $data['charge_horaire_td'] ?? 0,
            'nombre_groupe_cours' => $data['nombre_groupe_cours'] ?? 1,
            'nombre_groupe_tp' => $data['nombre_groupe_tp'] ?? 1,
            'nombre_groupe_td' => $data['nombre_groupe_td'] ?? 1,
            'semestre_id' => $data['semestre_id'],
            'annee_universitaire' => $data['annee_universitaire']
        ];

        return $this->insert($insertData);
    }

    /**
     * Mettre à jour un module
     */
    public function updateModule($id, $data) {
        // Vérifier si le module existe
        $existing = $this->findOne($id);
        if (!$existing) {
            throw new Exception("Module non trouvé");
        }

        // Vérifier que le semestre existe si fourni
        if (isset($data['semestre_id']) && !empty($data['semestre_id'])) {
            $this->checkSemestreExists($data['semestre_id']);
        }

        // Vérifier que la filière existe si fournie
        if (isset($data['filiere_id']) && !empty($data['filiere_id'])) {
            $this->checkFiliereExists($data['filiere_id']);
        }

        // Vérifier les doublons (exclure l'ID actuel)
        $this->checkDuplicates($data, $id);

        // Préparer les données avec les bons noms de colonnes
        $updateData = [];
        $fieldMapping = [
            'libelle' => 'libelle',
            'code' => 'code',
            'description' => 'description',
            'filiere_id' => 'filiere_id',
            'charge_horaire_cours' => 'charge_horaire_cours',
            'charge_horaire_tp' => 'charge_horaire_tp',
            'charge_horaire_td' => 'charge_horaire_td',
            'nombre_groupe_cours' => 'nombre_groupe_cours',
            'nombre_groupe_tp' => 'nombre_groupe_tp',
            'nombre_groupe_td' => 'nombre_groupe_td',
            'semestre_id' => 'semestre_id',
            'annee_universitaire' => 'annee_universitaire'
        ];

        foreach ($fieldMapping as $key => $dbField) {
            if (isset($data[$key])) {
                $updateData[$dbField] = $data[$key];
            }
        }

        return $this->update($id, $updateData);
    }

    /**
     * Vérifier que le semestre existe
     */
    private function checkSemestreExists($semestreId) {
        $sql = "SELECT id FROM semestre WHERE id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $semestreId]);
        if (!$stmt->fetch()) {
            throw new Exception("Le semestre spécifié n'existe pas");
        }
    }

    /**
     * Vérifier que la filière existe
     */
    private function checkFiliereExists($filiereId) {
        $sql = "SELECT id FROM filiere WHERE id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $filiereId]);
        if (!$stmt->fetch()) {
            throw new Exception("La filière spécifiée n'existe pas");
        }
    }

    /**
     * Vérifier les doublons
     */
    private function checkDuplicates($data, $excludeId = null) {
        $uniqueFields = ['code'];

        foreach ($uniqueFields as $field) {
            if (isset($data[$field]) && !empty($data[$field])) {
                $criteria = [$field => $data[$field]];
                $existing = $this->findOneBy($criteria);

                if ($existing && ($excludeId === null || $existing['id'] != $excludeId)) {
                    throw new Exception("Le champ '$field' est déjà utilisé par un autre module");
                }
            }
        }
    }

    /**
     * Supprimer un module
     */
    public function deleteModule($id) {
        // Vérifier si le module est utilisé par des notes
        $sql = "SELECT COUNT(*) as count FROM note WHERE module_id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        $result = $stmt->fetch();

        if ($result['count'] > 0) {
            throw new Exception("Ce module est utilisé par des notes et ne peut pas être supprimé");
        }

        // Vérifier si le module est utilisé par des emplois du temps
        $sql = "SELECT COUNT(*) as count FROM emploi WHERE module_id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        $result = $stmt->fetch();

        if ($result['count'] > 0) {
            throw new Exception("Ce module est utilisé dans des emplois du temps et ne peut pas être supprimé");
        }

        return $this->delete($id);
    }

    /**
     * Obtenir les statistiques
     */
    public function getStats() {
        $sql = "SELECT
                    COUNT(*) as total,
                    COUNT(DISTINCT filiere_id) as total_filieres,
                    SUM(charge_horaire_cours) as total_cours,
                    SUM(charge_horaire_tp) as total_tp,
                    SUM(charge_horaire_td) as total_td,
                    SUM(charge_horaire_cours + charge_horaire_tp + charge_horaire_td) as total_heures
                FROM module";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute();
        return $stmt->fetch();
    }

    /**
     * Obtenir toutes les filières (pour les selects)
     */
    public function getFilieres() {
        $sql = "SELECT id, libelle, code FROM filiere ORDER BY libelle ASC";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    /**
     * Obtenir toutes les années universitaires distinctes
     */
    public function getAnneesUniversitaires() {
        $sql = "SELECT DISTINCT annee_universitaire FROM module ORDER BY annee_universitaire DESC";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    /**
     * Exporter les modules (CSV)
     */
    public function exportToCsv() {
        $modules = $this->findWithRelations([], null, null);

        if (empty($modules)) {
            return null;
        }

        $csv = "ID,Libellé,Code,Filière,Semestre,Année,Description,Cours,TP,TD,Nb Groupes Cours,Nb Groupes TP,Nb Groupes TD\n";
        foreach ($modules as $m) {
            $csv .= implode(',', [
                $m['id'],
                '"' . addslashes($m['libelle']) . '"',
                '"' . addslashes($m['code']) . '"',
                '"' . addslashes($m['filiere_libelle'] ?? '') . '"',
                '"' . addslashes($m['semestre_libelle'] ?? '') . '"',
                $m['annee_universitaire'] ?? '',
                '"' . addslashes($m['description'] ?? '') . '"',
                $m['charge_horaire_cours'] ?? 0,
                $m['charge_horaire_tp'] ?? 0,
                $m['charge_horaire_td'] ?? 0,
                $m['nombre_groupe_cours'] ?? 1,
                $m['nombre_groupe_tp'] ?? 1,
                $m['nombre_groupe_td'] ?? 1
            ]) . "\n";
        }

        return $csv;
    }

    /**
     * Obtenir tous les modules (pour les selects)
     */
    public function getAllModules() {
        $sql = "SELECT m.*,
                       f.libelle as filiere_libelle,
                       s.libelle as semestre_libelle
                FROM module m
                LEFT JOIN filiere f ON m.filiere_id = f.id
                LEFT JOIN semestre s ON m.semestre_id = s.id
                ORDER BY f.libelle ASC, m.libelle ASC";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    /**
     * Obtenir le total d'heures pour un semestre
     */
    public function getTotalHeuresBySemestre($semestreId) {
        $sql = "SELECT
                    SUM(charge_horaire_cours) as total_cours,
                    SUM(charge_horaire_tp) as total_tp,
                    SUM(charge_horaire_td) as total_td,
                    SUM(charge_horaire_cours + charge_horaire_tp + charge_horaire_td) as total
                FROM module
                WHERE semestre_id = :semestre_id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':semestre_id' => $semestreId]);
        return $stmt->fetch();
    }

    /**
     * Vérifier si un module existe déjà dans un semestre
     */
    public function moduleExistsInSemestre($code, $semestreId) {
        $sql = "SELECT COUNT(*) as count FROM module WHERE code = :code AND semestre_id = :semestre_id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([
            ':code' => $code,
            ':semestre_id' => $semestreId
        ]);
        $result = $stmt->fetch();
        return $result['count'] > 0;
    }

    /**
     * Obtenir les modules par filière et semestre
     */
    public function getModulesByFiliereAndSemestre($filiereId, $semestreId) {
        $sql = "SELECT m.*,
                       s.libelle as semestre_libelle,
                       f.libelle as filiere_libelle
                FROM module m
                LEFT JOIN semestre s ON m.semestre_id = s.id
                LEFT JOIN filiere f ON m.filiere_id = f.id
                WHERE m.filiere_id = :filiere_id AND m.semestre_id = :semestre_id
                ORDER BY m.libelle ASC";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([
            ':filiere_id' => $filiereId,
            ':semestre_id' => $semestreId
        ]);
        return $stmt->fetchAll();
    }

    /**
     * Obtenir le nombre total de groupes pour un module
     */
    public function getTotalGroupes($moduleId) {
        $module = $this->findOne($moduleId);
        if (!$module) {
            return null;
        }

        return [
            'cours' => $module['nombre_groupe_cours'] ?? 1,
            'tp' => $module['nombre_groupe_tp'] ?? 1,
            'td' => $module['nombre_groupe_td'] ?? 1,
            'total' => ($module['nombre_groupe_cours'] ?? 1) +
                       ($module['nombre_groupe_tp'] ?? 1) +
                       ($module['nombre_groupe_td'] ?? 1)
        ];
    }
}