<?php


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

class EchelonService extends AbstractPdo {
    protected $table = 'echelon';
    protected $primaryKey = 'id';

    public function __construct()
    {
        parent::__construct();
    }
    /**
     * Rechercher des échelons avec critères
     */
    public function search($searchData, $page = 1, $perPage = 10) {
        $filters = [];

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

        // Filtre par echelle_id (recherche exacte avec select)
        if (!empty($searchData['echelle_id'])) {
            $filters['echelle_id'] = [
                'value' => $searchData['echelle_id'],
                'like' => false
            ];
        }

        // Filtres de la cascade Type Grade -> Grade (recherche exacte avec select)
        if (!empty($searchData['type_grade_id'])) {
            $filters['type_grade_id'] = [
                'value' => $searchData['type_grade_id'],
                'like' => false
            ];
        }
        if (!empty($searchData['grade_id'])) {
            $filters['grade_id'] = [
                'value' => $searchData['grade_id'],
                'like' => false
            ];
        }

        // Filtre par échelon suivant (recherche exacte avec select)
        if (!empty($searchData['echellon_suivant_id'])) {
            $filters['echellon_suivant_id'] = [
                'value' => $searchData['echellon_suivant_id'],
                'like' => false
            ];
        }

        $offset = ($page - 1) * $perPage;
        $echellons = $this->findWithEchelle($filters, $perPage, $offset);
        $total = $this->countWithEchelle($filters);

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

    /**
     * Find echellons with echelle information
     */
    public function findWithEchelle($filters = [], $limit = null, $offset = null) {
        $sql = "SELECT e.*, 
                       ec.libelle as echelle_libelle, 
                       ec.code as echelle_code,
                       g.id as grade_id,
                       g.libelle as grade_libelle,
                       g.code as grade_code,
                       tg.id as type_grade_id,
                       tg.libelle as type_grade_libelle,
                       tg.code as type_grade_code,
                       es.libelle as suivant_libelle,
                       es.code as suivant_code
                FROM echellon e 
                LEFT JOIN echelle ec ON e.echelle_id = ec.id
                LEFT JOIN grade g ON g.id = ec.grade_id
                LEFT JOIN type_grade tg ON tg.id = g.type_grade_id
                LEFT JOIN echellon es ON e.echellon_suivant_id = es.id";

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

        foreach ($filters as $key => $filter) {
            // Les critères de la cascade Type Grade -> Grade -> Échelle portent
            // sur les tables jointes, pas sur la table echellon elle-même.
            if ($key === 'type_grade_id') {
                $field = 'tg.id';
            } elseif ($key === 'grade_id') {
                $field = 'g.id';
            } else {
                $field = "e.{$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 e.echelle_id ASC, e.id 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 echellons with echelle information
     */
    public function countWithEchelle($filters = []) {
        $sql = "SELECT COUNT(*) as count 
                FROM echellon e 
                LEFT JOIN echelle ec ON e.echelle_id = ec.id
                LEFT JOIN grade g ON g.id = ec.grade_id
                LEFT JOIN type_grade tg ON tg.id = g.type_grade_id";

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

        foreach ($filters as $key => $filter) {
            if ($key === 'type_grade_id') {
                $field = 'tg.id';
            } elseif ($key === 'grade_id') {
                $field = 'g.id';
            } else {
                $field = "e.{$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 échelon par ID avec ses informations
     */
    public function getEchellon($id) {
        $sql = "SELECT e.*, 
                       ec.libelle as echelle_libelle, 
                       ec.code as echelle_code,
                       es.libelle as suivant_libelle,
                       es.code as suivant_code
                FROM echellon e 
                LEFT JOIN echelle ec ON e.echelle_id = ec.id
                LEFT JOIN echellon es ON e.echellon_suivant_id = es.id
                WHERE e.id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        return $stmt->fetch();
    }

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

    /**
     * Créer un nouvel échelon
     */
    public function createEchellon($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['echelle_id'])) {
            throw new Exception("L'échelle est obligatoire");
        }

        // Vérifier que l'échelle existe
        $this->checkEchelleExists($data['echelle_id']);

        // Vérifier que l'échelon suivant existe si fourni
        if (!empty($data['echellon_suivant_id'])) {
            $this->checkEchellonExists($data['echellon_suivant_id']);
        }

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

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

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

        // Vérifier que l'échelle existe si fournie
        if (isset($data['echelle_id']) && !empty($data['echelle_id'])) {
            $this->checkEchelleExists($data['echelle_id']);
        }

        // Vérifier que l'échelon suivant existe si fourni
        if (isset($data['echellon_suivant_id']) && !empty($data['echellon_suivant_id'])) {
            // Vérifier qu'on ne se pointe pas soi-même
            if ((int)$data['echellon_suivant_id'] === (int)$id) {
                throw new Exception("Un échelon ne peut pas avoir lui-même comme échelon suivant");
            }
            $this->checkEchellonExists($data['echellon_suivant_id']);
        }

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

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

    /**
     * Vérifier que l'échelle existe
     */
    private function checkEchelleExists($echelleId) {
        $sql = "SELECT id FROM echelle WHERE id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $echelleId]);
        if (!$stmt->fetch()) {
            throw new Exception("L'échelle spécifiée n'existe pas");
        }
    }

    /**
     * Vérifier que l'échelon existe
     */
    private function checkEchellonExists($echellonId) {
        $sql = "SELECT id FROM echellon WHERE id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $echellonId]);
        if (!$stmt->fetch()) {
            throw new Exception("L'échelon suivant spécifié 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 échelon");
                }
            }
        }

        // Vérifier l'unicité du libellé au sein d'une même échelle
        if (isset($data['libelle']) && isset($data['echelle_id']) && !empty($data['echelle_id'])) {
            $sql = "SELECT id FROM echellon 
                    WHERE libelle = :libelle 
                    AND echelle_id = :echelle_id 
                    AND id != :exclude_id";
            $stmt = $this->pdo->prepare($sql);
            $params = [
                ':libelle' => $data['libelle'],
                ':echelle_id' => $data['echelle_id'],
                ':exclude_id' => $excludeId ?? 0
            ];
            $stmt->execute($params);
            if ($stmt->fetch()) {
                throw new Exception("Un échelon avec ce libellé existe déjà dans cette échelle");
            }
        }

        // Vérifier l'unicité du code au sein d'une même échelle
        if (isset($data['code']) && isset($data['echelle_id']) && !empty($data['echelle_id'])) {
            $sql = "SELECT id FROM echellon 
                    WHERE code = :code 
                    AND echelle_id = :echelle_id 
                    AND id != :exclude_id";
            $stmt = $this->pdo->prepare($sql);
            $params = [
                ':code' => $data['code'],
                ':echelle_id' => $data['echelle_id'],
                ':exclude_id' => $excludeId ?? 0
            ];
            $stmt->execute($params);
            if ($stmt->fetch()) {
                throw new Exception("Un échelon avec ce code existe déjà dans cette échelle");
            }
        }
    }

    /**
     * Supprimer un échelon
     */
    public function deleteEchellon($id) {
        // Vérifier si l'échelon est utilisé par des professeurs
        $sql = "SELECT COUNT(*) as count FROM professeur WHERE echellon_id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        $result = $stmt->fetch();

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

        // Vérifier si l'échelon est utilisé comme échelon suivant
        $sql = "SELECT COUNT(*) as count FROM echellon WHERE echellon_suivant_id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':id' => $id]);
        $result = $stmt->fetch();

        if ($result['count'] > 0) {
            throw new Exception("Cet échelon est référencé comme échelon suivant par d'autres échelons et ne peut pas être supprimé");
        }

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

    /**
     * Obtenir les statistiques
     */
    public function getStats() {
        $sql = "SELECT COUNT(*) as total FROM {$this->table}";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute();
        $result = $stmt->fetch();

        // Nombre d'échelons par échelle
        $sql2 = "SELECT echelle_id, COUNT(*) as count 
                 FROM echellon 
                 GROUP BY echelle_id 
                 ORDER BY count DESC 
                 LIMIT 1";
        $stmt2 = $this->pdo->prepare($sql2);
        $stmt2->execute();
        $maxEchellon = $stmt2->fetch();

        return [
            'total' => (int)$result['total'],
            'max_echellons_per_echelle' => $maxEchellon ? (int)$maxEchellon['count'] : 0
        ];
    }

    /**
     * Exporter les échelons (CSV)
     */
    public function exportToCsv() {
        $echellons = $this->findWithEchelle([], null, null);

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

        $csv = "ID,Libellé,Code,Échelle,Description,Échelon Suivant,Date de création,Date de mise à jour\n";
        foreach ($echellons as $e) {
            $csv .= implode(',', [
                    $e['id'],
                    '"' . addslashes($e['libelle']) . '"',
                    '"' . addslashes($e['code']) . '"',
                    '"' . addslashes($e['echelle_libelle'] ?? '') . '"',
                    '"' . addslashes($e['description'] ?? '') . '"',
                    '"' . addslashes($e['suivant_libelle'] ?? 'Aucun') . '"',
                    $e['created_at'] ?? '',
                    $e['updated_at'] ?? ''
                ]) . "\n";
        }

        return $csv;
    }

    /**
     * Obtenir tous les échelons (pour les selects)
     */
    public function getAllEchellons() {
        return $this->findAll('echelle_id ASC, id ASC');
    }

    /**
     * Obtenir les échelons par échelle
     */
    public function getEchellonsByEchelle($echelleId) {
        $sql = "SELECT * FROM echellon WHERE echelle_id = :echelle_id ORDER BY libelle ASC";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([':echelle_id' => $echelleId]);
        return $stmt->fetchAll();
    }

    /**
     * Obtenir l'échelon suivant
     */
    public function getEchellonSuivant($id) {
        $echellon = $this->findOne($id);
        if (!$echellon || !$echellon['echellon_suivant_id']) {
            return null;
        }
        return $this->findOne($echellon['echellon_suivant_id']);
    }

    /**
     * Obtenir la chaîne des échelons depuis un point de départ
     */
    public function getEchellonChain($startId) {
        $chain = [];
        $currentId = $startId;
        $visited = [];

        while ($currentId && !isset($visited[$currentId])) {
            $visited[$currentId] = true;
            $echellon = $this->findOne($currentId);
            if (!$echellon) break;

            $chain[] = $echellon;
            $currentId = $echellon['echellon_suivant_id'];
        }

        return $chain;
    }

    /**
     * Vérifier si un échelon est le dernier de sa chaîne
     */
    public function isLastEchellon($id) {
        $echellon = $this->findOne($id);
        if (!$echellon) {
            throw new Exception("Échelon non trouvé");
        }
        return empty($echellon['echellon_suivant_id']);
    }

    /**
     * Mettre à jour uniquement l'échelon suivant
     */
    public function updateEchellonSuivant($id, $suivantId) {
        if ($suivantId && (int)$suivantId === (int)$id) {
            throw new Exception("Un échelon ne peut pas avoir lui-même comme échelon suivant");
        }

        if ($suivantId) {
            $this->checkEchellonExists($suivantId);
        }

        $sql = "UPDATE echellon SET echellon_suivant_id = :suivant_id WHERE id = :id";
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([
            ':id' => $id,
            ':suivant_id' => $suivantId
        ]);

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