import json

########### Fonctions données ###########


def chargement_json(nom_fichier):
    """Charge le contenu d'un fichier JSON dans un dictionnaire Python renvoyé"""
    with open(nom_fichier, "r", encoding="utf8") as curseur:
        return json.load(curseur)


def est_dictionnaire(objet):
    """Teste si un objet est de type dictionnaire"""
    return isinstance(objet, dict)


##########################################


# Première fonction à implémenter après avoir découvert le fichier JSON agrégé
# Cf fichier `empreinte_ada_agr.json`
def total_simple(empreinte):
    """Fonction qui renvoie l'empreinte carbone totale d'un dictionnaire associant
    une empreinte carbone à des noms de catégories"""
    total = 0                                    # le total, vide au départ
    for categorie in empreinte:                  # on parcourt les catégories une à une
        total = total + empreinte[categorie]     # et on ajoute l'empreinte de la catégorie
    return total


# Deuxième fonction : il faut la récursivité pour le cas des sous-catégories
# Cf fichier `empreinte_ada.json`
def total_rec(empreinte):
    """Fonction récursive qui renvoie l'empreinte carbone totale représentée
    par un dictionnaire dont les valeurs peuvent aussi être des dictionnaires"""
    total = 0                                  # le total, vide au départ
    for categorie in empreinte:                # on parcourt les clés
        valeur = empreinte[categorie]          # la valeur associée à la clé
        if est_dictionnaire(valeur):           # une sous-catégorie : on recommence dedans
            total = total + total_rec(valeur)  # et on ajoute son total
        else:                                  # un nombre : on l'ajoute directement
            total = total + valeur
    return total


def test_total_rec():
    test_dico1 = {"a": 1, "d": 2}
    assert total_rec(test_dico1) == 3
    test_dico2 = {"a": {"b": 1, "c": 2}, "d": {"e": 3}}
    assert total_rec(test_dico2) == 6
    test_dico3 = {"Logement": {"Energie": {"Electricité": 206, "Cuisson": 105},
                               "Construction": 650},
                  "Transport": 14}       # trois niveaux, comme dans le fichier d'Ada
    assert total_rec(test_dico3) == 975  # 206 + 105 + 650 + 14

# ==========================================
# Fonction à analyser et corriger (Question 3)
# ==========================================

# Version d'origine, fournie par le sujet, conservée pour mémoire :
#
# def alerte_valeur_aberrante(empreinte, limite):
#     for categorie, valeur in empreinte.items():
#         if est_dictionnaire(valeur):
#             return alerte_valeur_aberrante(valeur, limite)   # <- la ligne fautive
#         else:
#             if valeur > limite:
#                 return True
#     return False
#
# Le return devant l'appel récursif fait sortir de la boucle dès la première
# sous-catégorie, même quand elle ne contient aucune alerte : les clés
# suivantes ne sont jamais regardées.


def alerte_valeur_aberrante(empreinte, limite):
    """
    Fonction censée déterminer si au moins une valeur du dictionnaire
    dépasse strictement la limite donnée.
    """
    for categorie, valeur in empreinte.items():
        if est_dictionnaire(valeur):
            if alerte_valeur_aberrante(valeur, limite):  # une alerte dans la sous-catégorie ?
                return True                              # si oui : on s'arrête tout de suite
        else:
            if valeur > limite:                          # un nombre au-dessus de la limite ?
                return True
    return False


# ==========================================
# Jeu de tests (Question 4)
# ==========================================


def test_alerte():
    assert alerte_valeur_aberrante({}, 10) == False                       # vide
    assert alerte_valeur_aberrante({"a": 5, "b": 8}, 10) == False         # plat, rien au-dessus
    assert alerte_valeur_aberrante({"a": 5, "b": 20}, 10) == True         # plat, une valeur au-dessus
    assert alerte_valeur_aberrante({"a": 10}, 10) == False                # égal à la limite
    assert alerte_valeur_aberrante({"a": {"b": 20}}, 10) == True          # alerte dans une sous-catégorie
    assert alerte_valeur_aberrante({"a": {"b": 1}, "c": 20}, 10) == True  # le bug de la question 3
    assert alerte_valeur_aberrante({"a": {"b": {"c": 20}}}, 10) == True   # trois niveaux
    print("tous les tests passent")


# ==========================================
# Tests de l'article
# ==========================================

# Question 1
empreinte_agr = chargement_json("empreinte_ada_agr.json")     # on lit le fichier agrégé
print(total_simple(empreinte_agr))                            # et on affiche le total d'Ada

# Question 2
test_total_rec()                                         # aucune erreur : les trois assert passent
empreinte_brute = chargement_json("empreinte_ada.json")  # on lit le fichier détaillé
print(total_rec(empreinte_brute))                        # attendu : le 7252 de la question 1

# Question 3
print(alerte_valeur_aberrante(empreinte_brute, 1000))  # attendu : True, le chauffage à 1500
print(alerte_valeur_aberrante(empreinte_brute, 1499))  # attendu : True, 1500 > 1499
print(alerte_valeur_aberrante(empreinte_brute, 1500))  # attendu : False, 1500 n'est pas strictement supérieur à 1500

# Question 4
test_alerte()
