# agi_lab/evaluation/metrics.py

import numpy as np

class EvaluationMetrics:
    """
    Clase utilitaria para calcular métricas de evaluación de AGI:
    - Generalidad de tarea
    - Transferencia
    - Autonomía
    - Robustez
    - Explicabilidad
    """

    @staticmethod
    def generality_score(agent, tasks, threshold=0.8):
        """
        Mide en cuántas tareas el agente supera cierto umbral de desempeño.
        """
        success_count = 0
        for env in tasks:
            obs = env.reset()
            total_reward = 0
            done = False
            while not done:
                agent.perceive(obs)
                action = agent.decide()
                obs, reward, done, _ = env.step(action)
                agent.learn(reward, done)
                total_reward += reward
            if total_reward >= threshold:
                success_count += 1
        return success_count / len(tasks)

    @staticmethod
    def transfer_score(agent, task_seq):
        """
        Evalúa cuánto conocimiento se transfiere entre tareas.
        """
        deltas = []
        for i in range(1, len(task_seq)):
            env = task_seq[i]
            obs = env.reset()
            reward_pre = 0
            for _ in range(10):  # sin entrenamiento
                agent.perceive(obs)
                action = agent.decide()
                obs, reward, done, _ = env.step(action)
                reward_pre += reward
                if done: break

            agent.reset()
            obs = env.reset()
            reward_post = 0
            for _ in range(10):  # con entrenamiento mínimo
                agent.perceive(obs)
                action = agent.decide()
                obs, reward, done, _ = env.step(action)
                agent.learn(reward)
                reward_post += reward
                if done: break

            delta = (reward_post - reward_pre) / max(reward_post, 1e-6)
            deltas.append(delta)
        return np.mean(deltas)

    @staticmethod
    def robustness_score(agent, env, perturb_fn, runs=5):
        """
        Mide estabilidad bajo perturbaciones del entorno.
        """
        drops = []
        for _ in range(runs):
            obs = env.reset()
            total_normal = 0
            done = False
            while not done:
                agent.perceive(obs)
                action = agent.decide()
                obs, reward, done, _ = env.step(action)
                agent.learn(reward)
                total_normal += reward

            obs = env.reset()
            env = perturb_fn(env)
            total_perturbed = 0
            done = False
            while not done:
                agent.perceive(obs)
                action = agent.decide()
                obs, reward, done, _ = env.step(action)
                total_perturbed += reward

            drop = (total_normal - total_perturbed) / max(total_normal, 1e-6)
            drops.append(drop)
        return 1 - np.mean(drops)

    @staticmethod
    def explainability_score(symbolic_model):
        """
        Mide explicabilidad por compresibilidad lógica.
        Asume que el modelo simbólico es un string o estructura parseable.
        """
        length = len(str(symbolic_model))
        compressed = len(str(hash(symbolic_model)))
        return compressed / max(length, 1)