#
# Erminig - Librairie Pakfile
# Copyright (C) 2025 L0m1g
# Sous licence DOUARN - Voir le fichier LICENCE pour les détails
#
# Ce fichier fait partie du projet Erminig.
# Libre comme l’air, stable comme un menhir, et salé comme le beurre.
#

import os
import toml

class Pakfile:
    """
    Représente un fichier de description de paquet (pakfile.toml)
    dans le système de gestion de paquets Erminig.

    Cette classe offre les opérations essentielles pour manipuler,
    vérifier et modifier les pakfiles.
    """

    BASE_PATH = "/var/govel" 

    def __init__(self, package: str):
        """
        Initialise la gestion du pakfile pour un paquet donné.

        :param package: Nom du paquet
        """
        self.package = package
        self.data = {}

    def _load(self):
        """
        Charge le contenu du pakfile en mémoire
        Appelé automatiquement à l'initialisation si le fichier existe
        """
        package_dir, pakfile_path = self._get_paths(package)
        if not os.path.exists(pakfile_path):
            raise FileNotFoundError(f"Pakfile introuvable pour {self.package} dans {pakfile_path}")

        with open(pakfile_path, "r", encoding="utf-8") as f:
            self.data = toml.load(f)

    def _get_paths(self, package: str) -> tuple[str, str]:
        """
        Retourne le chemin du dossier du paquet et du pakfile associé.
        """
        package_dir = f"{self.BASE_PATH}/{package}"
        pakfile_path = f"{package_dir}/pakfile.toml"
        return package_dir, pakfile_path

    def new(self, package: str):
        """
        Crée un nouveau pakfile avec un squelette de base prêt à être édité.
        Soulève une erreur si le pakfile existe déjà.
        """
        package_dir, pakfile_path = self._get_paths(package)

        if os.path.exists(pakfile_path):
            raise FileExistsError(f"Le pakfile existe déjà pour {package}")

        data = {
            "header": {
                "copyright": "Copyright (C) 2025 L0m1g",
                "license": "Sous licence DOUARN - Voir le fichier LICENCE pour les détails",
                "author": "L0m1g",
                "maintainer": "L0m1g",
                "description": "Description à compléter"
            },
            "name": package,
            "ver": "",
            "rev": 1,
            "src": [],
            "build": "# Ajouter les commandes de construction ici",
            "check": "# Ajouter les commandes check ou équivalentes ici",
            "install": "# Ajouter les commandes d’installation ici"
        }

        os.makedirs(package_dir, exist_ok=True)

        with open(pakfile_path, "w", encoding="utf-8") as f:
            toml.dump(data, f)

    def set(self, key: str, value):
        """
        Définit une valeur dans le pakfile et sauvegarde immédiatement
        """
        self.data[key] = value
        self._save()

    def _save(self):
        """
        Ecrit le contenu actuel de self.data dans le pakfile correspondant
        Créé le répertoire s'il n'existe pas.
        """
        package_dir, pakfile_path = self._get_paths(self.package)

        os.makedirs(package_dir, exist_ok=True)
        with open(pakfile_path, "w", encoding="utf-8") as f:
            toml.dump(self.data, f)

    def get(self, key: str):
        """
        Récupère la valeur d'une clé dans le pakfile
        Redirige vers de getters spécifiques

        :param key: Clé à récupérer
        :return: Valeur associée ou None
        """
        match key:
            case "name" | "ver" | "rev":
                return self._get(key)
            case "src" | "deps" | "bdeps":
                return self._get_list(key)
            case _:
                return None

    def _get(self, key: str) -> str | None:
        """
        Getter générique pour les chaines
        """
        return self.data.get(key)

    def _get_list(self, key: str) -> list[str]:
        """
        Getter spécifique aux listes
        """
        return self.data.get(key,[])

    def delete(self, package: str) -> bool:
        """
        Supprime un paquet non installé et son répertoire associé.

        :param package: Nom du paquet à supprimer
        :return: True si la suppression a réussi, False sinon
        """
        package_dir, pakfile_path = self._get_paths(package)

        if not os.path.exists(package_dir):
            print(f"Le paquet '{package}' n'existe pas. Rien à supprimer.")
            return False

        try:
            # On vire tout le répertoire et son contenu
            for root, dirs, files in os.walk(package_dir, topdown=False):
                for file in files:
                    os.remove(os.path.join(root, file))
                for dir in dirs:
                    os.rmdir(os.path.join(root, dir))
            os.rmdir(package_dir)

            print(f"Paquet '{package}' supprimé avec succès.")
            return True

        except Exception as e:
            print(f"Erreur lors de la suppression de '{package}': {e}")
            return False

    def check(self):
        """
        Vérifie la validité de la structure du pakfile.
        Contrôle la présence et la cohérence des clés obligatoires.
        Ne vérifie PAS la disponibilité des sources ou la qualité du code, juste la structure.
        """
        required_keys = {"name", "ver", "rev", "src"}
        optional_keys = {"deps", "bdeps", "build", "make", "install"}

        all_keys = set(self.data.keys())
        unknown_keys = all_keys - required_keys - optional_keys

        missing_keys = required_keys - all_keys

        if missing_keys:
            print(f"Clés manquantes dans {self.package}: {', '.join(missing_keys)}")
            return False

        if unknown_keys:
            print(f"Clés inconnues dans {self.package}: {', '.join(unknown_keys)}")
            return False

        return True
