module TypeTracer

using Serialization

# Singleton pour stocker les signatures uniques (Thread-safe)
const SEEN_SIGNATURES = Set{String}()
const LOCK = ReentrantLock()
const LOG_FILE = "execution_trace.txt"

"""
Fonction interne pour enregistrer une signature.
Format: FunctionName(ArgType1, ArgType2, ...) -> ReturnType
"""
function _record_call(func_name, args, result)
    # On map les types Julia vers des chaînes de caractères
    arg_types = join([string(typeof(a)) for a in args], ", ")
    ret_type = string(typeof(result))
    
    signature = "$func_name($arg_types) -> $ret_type"

    # Vérification avec lock pour le multi-threading (fréquent en simu)
    lock(LOCK) do
        if !(signature in SEEN_SIGNATURES)
            push!(SEEN_SIGNATURES, signature)
            # On écrit immédiatement pour ne rien perdre en cas de crash
            open(LOG_FILE, "a") do io
                println(io, signature)
            end
            # Un petit echo console pour voir que ça vit
            println("[TRACER] Nouvelle variante capturée : $signature")
        end
    end
end

"""
Macro à placer devant les définitions de fonctions.
Elle enveloppe le corps de la fonction pour logger les types.
"""
macro monitor(expr)
    # Vérifie qu'on applique bien la macro sur une définition de fonction
    if expr.head !== :function && expr.head !== :(=)
        error("La macro @monitor doit être appliquée à une définition de fonction.")
    end

    # Extraction de la signature (nom + args) et du corps
    call_def = expr.args[1]
    body = expr.args[2]

    # Récupération du nom de la fonction (gestion cas f(x) et Module.f(x))
    func_name = length(call_def.args) >= 1 ? call_def.args[1] : :anonymous
    
    # On reconstruit la fonction avec l'instrumentation
    quote
        function $(call_def)
            # 1. Exécuter le corps original et capturer le résultat
            __result = $(body)
            
            # 2. Récupérer les arguments (via ... dans la macro c'est délicat, 
            # donc on utilise une astuce : on capture les valeurs des arguments par leur nom)
            # Note : Pour une conversion C, on a besoin des types concrets à l'exécution.
            # Ici, on passe un tuple des arguments.
            # Attention : cela suppose que call_def.args contient les noms des variables.
            
            # Pour faire simple et robuste sans métaprogrammation complexe sur les arguments :
            # On logue juste le fait qu'on est passé ici.
            # MAIS pour avoir les args, il faut être plus malin.
            
            # Approche simplifiée : On logue à la fin
            TypeTracer._record_call(
                $(string(func_name)), 
                # On capture tous les arguments de la fonction courante
                # (nécessite que la fonction ne soit pas anonyme pure)
                (Base.@locals()...,), # Capture tout le scope local (arguments inclus)
                __result
            )
            
            return __result
        end
    end |> esc
end

# Fonction de nettoyage pour vider le log au début
function init_trace()
    if isfile(LOG_FILE)
        rm(LOG_FILE)
    end
    println("--- Démarrage du traçage des types ---")
end

end