from __future__ import annotations

import json
from pathlib import Path
from decorateur_Dt import *


FICHIER_JSON = Path("bibliotheque_reelle.json")


class Auteur:
    _prochain_id: int = 1

    def __init__(self, prenom: str, nom: str, id_auteur: int | None = None) -> None:
        if id_auteur is None:
            self.id = Auteur._prochain_id
            Auteur._prochain_id += 1
        else:
            self.id = id_auteur
            Auteur._prochain_id = max(Auteur._prochain_id, id_auteur + 1)

        self.prenom: str = prenom
        self.nom: str = nom

    def __str__(self) -> str:
        return f"{self.id} - {self.prenom} {self.nom}"


class Livre:
    _prochain_id: int = 1

    def __init__(
        self,
        titre: str,
        auteur: Auteur,
        id_livre: int | None = None,
        disponible: bool = True,
    ) -> None:
        if id_livre is None:
            self.id = Livre._prochain_id
            Livre._prochain_id += 1
        else:
            self.id = id_livre
            Livre._prochain_id = max(Livre._prochain_id, id_livre + 1)

        self.titre: str = titre
        self.auteur: Auteur = auteur
        self.disponible: bool = disponible

    def emprunter(self) -> bool:
        if not self.disponible:
            return False

        self.disponible = False
        return True

    def rendre(self) -> bool:
        if self.disponible:
            return False

        self.disponible = True
        return True

    def __str__(self) -> str:
        statut: str = "disponible" if self.disponible else "emprunté"

        return (
            f"{self.id} - {self.titre} - "
            f"{self.auteur.prenom} {self.auteur.nom} - {statut}"
        )


class Bibliotheque:
    def __init__(self) -> None:
        self.auteurs: list[Auteur] = []
        self.livres: list[Livre] = []

        # Chargement des données existantes au démarrage
        self.charger()

    # ========================================================
    # PERSISTANCE JSON
    # ========================================================

    def sauvegarder(self) -> None:
        """
        Enregistre les auteurs et les livres dans le fichier JSON.
        """

        donnees = {
            "auteurs": [
                {
                    "id": auteur.id,
                    "prenom": auteur.prenom,
                    "nom": auteur.nom,
                }
                for auteur in self.auteurs
            ],
            "livres": [
                {
                    "id": livre.id,
                    "titre": livre.titre,
                    "auteur_id": livre.auteur.id,
                    "disponible": livre.disponible,
                }
                for livre in self.livres
            ],
        }

        with FICHIER_JSON.open("w", encoding="utf-8") as fichier:
            json.dump(donnees, fichier, ensure_ascii=False, indent=4)

    def charger(self) -> None:
        """
        Charge les auteurs et les livres depuis le fichier JSON.

        Si le fichier n'existe pas encore, la bibliothèque démarre vide.
        """

        if not FICHIER_JSON.exists():
            return

        try:
            with FICHIER_JSON.open("r", encoding="utf-8") as fichier:
                donnees = json.load(fichier)

        except (json.JSONDecodeError, OSError) as erreur:
            print(f"Erreur lors du chargement du fichier JSON : {erreur}")
            return

        # Reconstruction des auteurs
        for donnees_auteur in donnees.get("auteurs", []):
            auteur = Auteur(
                prenom=donnees_auteur["prenom"],
                nom=donnees_auteur["nom"],
                id_auteur=donnees_auteur["id"],
            )

            self.auteurs.append(auteur)

        # Reconstruction des livres
        for donnees_livre in donnees.get("livres", []):
            auteur = self.trouver_auteur(donnees_livre["auteur_id"])

            if auteur is None:
                continue

            livre = Livre(
                titre=donnees_livre["titre"],
                auteur=auteur,
                id_livre=donnees_livre["id"],
                disponible=donnees_livre["disponible"],
            )

            self.livres.append(livre)

        print("Bibliothèque chargée depuis le fichier JSON.")

    # ========================================================
    # AUTEURS
    # ========================================================

    def ajouter_auteur(self, auteur: Auteur) -> bool:
        """
        Ajoute un auteur à la bibliothèque.

        Les homonymes sont autorisés.
        """

        self.auteurs.append(auteur)

        # Mise à jour du fichier JSON
        self.sauvegarder()

        return True

    def trouver_auteur(self, id_auteur: int) -> Auteur | None:
        for auteur in self.auteurs:
            if auteur.id == id_auteur:
                return auteur

        return None

    # ========================================================
    # LIVRES
    # ========================================================

    def ajouter_livre(self, livre: Livre) -> bool:
        """
        Ajoute un livre si son auteur est enregistré
        dans la bibliothèque.
        """

        auteur: Auteur | None = self.trouver_auteur(livre.auteur.id)

        if auteur is None:
            return False

        self.livres.append(livre)

        # Mise à jour du fichier JSON
        self.sauvegarder()

        return True

    def trouver_livre(self, id_livre: int) -> Livre | None:
        for livre in self.livres:
            if livre.id == id_livre:
                return livre

        return None
    
    @mesurer_temps
    def trouver_livres_auteur(self, prenom: str, nom: str) -> list :
        lst_livres = []
        for livre in self.livres:
            if livre.auteur.prenom == prenom and livre.auteur.nom == nom :
                lst_livres.append(livre)
        return lst_livres

    # ========================================================
    # EMPRUNT / RESTITUTION
    # ========================================================

    def emprunter_livre(self, id_livre: int) -> bool:
        livre = self.trouver_livre(id_livre)

        if livre is None:
            print("Livre non trouvé.")
            return False

        if not livre.emprunter():
            print("Désolé, ce livre est déjà emprunté.")
            return False

        # Mise à jour du fichier JSON
        self.sauvegarder()

        print(f"Emprunt effectué : {livre.titre}")
        return True

    def rendre_livre(self, id_livre: int) -> bool:
        livre = self.trouver_livre(id_livre)

        if livre is None:
            print("Livre non trouvé.")
            return False

        if not livre.rendre():
            print("Ce livre est déjà disponible.")
            return False

        # Mise à jour du fichier JSON
        self.sauvegarder()

        print(f"Restitution effectuée : {livre.titre}")
        return True

    # ========================================================
    # AFFICHAGE
    # ========================================================

    def afficher_auteurs(self) -> None:
        print("AUTEURS")
        print("-----------------------------------")

        for auteur in self.auteurs:
            print(auteur)

        print("-----------------------------------")

    def afficher_livres(self) -> None:
        print("LIVRES")
        print("-----------------------------------")

        for livre in self.livres:
            print(livre)

        print("-----------------------------------")
