"""
Tests pour le gestionnaire d'apprentissage.
"""

import pytest
import numpy as np
import tempfile
import shutil
from pathlib import Path
from datetime import datetime
from unittest.mock import Mock, MagicMock

from geniusia2.core.learning_manager import LearningManager
from geniusia2.core.models import Action, TaskProfile
from geniusia2.core.embeddings_manager import EmbeddingsManager
from geniusia2.core.logger import Logger


@pytest.fixture
def temp_dirs():
    """Crée des répertoires temporaires pour les tests."""
    temp_dir = tempfile.mkdtemp()
    profiles_dir = Path(temp_dir) / "profiles"
    logs_dir = Path(temp_dir) / "logs"
    index_dir = Path(temp_dir) / "index"
    keys_dir = Path(temp_dir) / "keys"
    
    profiles_dir.mkdir(parents=True, exist_ok=True)
    logs_dir.mkdir(parents=True, exist_ok=True)
    index_dir.mkdir(parents=True, exist_ok=True)
    keys_dir.mkdir(parents=True, exist_ok=True)
    
    yield {
        "profiles": str(profiles_dir),
        "logs": str(logs_dir),
        "index": str(index_dir),
        "keys": str(keys_dir)
    }
    
    shutil.rmtree(temp_dir)


@pytest.fixture
def mock_embeddings_manager(temp_dirs):
    """Crée un gestionnaire d'embeddings mocké."""
    return EmbeddingsManager(
        model_name="ViT-B-32",
        index_path=temp_dirs["index"],
        device="cpu"
    )


@pytest.fixture
def mock_logger(temp_dirs):
    """Crée un logger mocké."""
    return Logger(
        log_dir=temp_dirs["logs"],
        key_path=temp_dirs["keys"]
    )


@pytest.fixture
def learning_manager(mock_embeddings_manager, mock_logger, temp_dirs):
    """Crée une instance du gestionnaire d'apprentissage pour les tests."""
    config = {
        "thresholds": {
            "autopilot_observations": 20,
            "autopilot_concordance": 0.95,
            "confidence_min": 0.90,
            "rollback_confidence": 0.85
        }
    }
    
    return LearningManager(
        embeddings_manager=mock_embeddings_manager,
        logger=mock_logger,
        config=config,
        profiles_path=temp_dirs["profiles"]
    )


def test_initialization(learning_manager):
    """Test l'initialisation du gestionnaire."""
    assert learning_manager.mode == "shadow"
    assert len(learning_manager.tasks) == 0
    assert learning_manager.current_task_id is None


def test_observe_action(learning_manager):
    """Test l'enregistrement d'une observation."""
    action = Action(
        action_type="click",
        target_element="valider_button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test Window"
    )
    
    learning_manager.observe(action)
    
    # Vérifier qu'une tâche a été créée
    assert len(learning_manager.tasks) == 1
    
    # Vérifier les propriétés de la tâche
    task_id = list(learning_manager.tasks.keys())[0]
    task = learning_manager.tasks[task_id]
    
    assert task.observation_count == 1
    assert task.mode == "shadow"
    assert len(task.action_sequence) == 1
    assert task.window_whitelist == ["Test Window"]


def test_mode_transition_shadow_to_assist(learning_manager):
    """Test la transition de Shadow vers Assisté."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    # Observer 5 fois pour déclencher la transition
    for _ in range(5):
        learning_manager.observe(action)
    
    task_id = list(learning_manager.tasks.keys())[0]
    task = learning_manager.tasks[task_id]
    
    # Devrait être passé en mode Assisté
    assert task.mode == "assist"
    assert task.observation_count == 5


def test_calculate_confidence(learning_manager):
    """Test le calcul du score de confiance."""
    # Créer une tâche de test
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    
    # Calculer la confiance
    confidence = learning_manager.calculate_confidence(
        vision_conf=0.9,
        llm_score=0.8,
        task_id=task_id
    )
    
    # Vérifier la formule : 0.6 * 0.9 + 0.3 * 0.8 + 0.1 * 0.0 = 0.78
    assert 0.77 <= confidence <= 0.79


def test_confirm_action_accept(learning_manager):
    """Test la confirmation d'une action acceptée."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    
    # Accepter l'action
    learning_manager.confirm_action({
        "type": "accept",
        "task_id": task_id
    })
    
    task = learning_manager.tasks[task_id]
    assert task.concordance_rate == 1.0


def test_confirm_action_reject(learning_manager):
    """Test la confirmation d'une action rejetée."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    
    # Rejeter l'action
    learning_manager.confirm_action({
        "type": "reject",
        "task_id": task_id
    })
    
    task = learning_manager.tasks[task_id]
    assert task.concordance_rate == 0.0


def test_confirm_action_correct(learning_manager):
    """Test la correction d'une action."""
    action = Action(
        action_type="click",
        target_element="wrong_button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    
    # Corriger l'action
    corrected_action = Action(
        action_type="click",
        target_element="correct_button",
        bbox=(200, 100, 50, 30),
        confidence=0.95,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.confirm_action({
        "type": "correct",
        "task_id": task_id,
        "corrected_action": corrected_action
    })
    
    task = learning_manager.tasks[task_id]
    assert task.correction_count == 1
    assert len(task.action_sequence) == 2


def test_should_transition_to_auto(learning_manager):
    """Test la vérification des critères pour passer en Autopilot."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    # Observer 20 fois
    for _ in range(20):
        learning_manager.observe(action)
    
    task_id = list(learning_manager.tasks.keys())[0]
    task = learning_manager.tasks[task_id]
    
    # Définir une concordance élevée
    task.concordance_rate = 0.96
    
    # Devrait être éligible pour Autopilot
    assert learning_manager.should_transition_to_auto(task_id)


def test_rollback_if_low_confidence(learning_manager):
    """Test la rétrogradation en cas de confiance faible."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    task = learning_manager.tasks[task_id]
    
    # Forcer le mode Auto avec confiance faible
    task.mode = "auto"
    task.confidence_score = 0.85  # En dessous du seuil de 0.90
    
    learning_manager.rollback_if_low_confidence(task_id)
    
    # Devrait être rétrogradé en Assisté
    assert task.mode == "assist"


def test_evaluate_task(learning_manager):
    """Test l'évaluation d'une tâche."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    
    metrics = learning_manager.evaluate_task(task_id)
    
    assert "task_id" in metrics
    assert "task_name" in metrics
    assert "mode" in metrics
    assert "observation_count" in metrics
    assert "concordance_rate" in metrics
    assert "confidence_score" in metrics
    assert "correction_count" in metrics
    assert metrics["observation_count"] == 1


def test_get_all_tasks(learning_manager):
    """Test la récupération de toutes les tâches."""
    # Créer plusieurs tâches
    for i in range(3):
        action = Action(
            action_type="click",
            target_element=f"button_{i}",
            bbox=(100 + i*50, 100, 50, 30),
            confidence=0.9,
            embedding=np.random.rand(512).astype(np.float32),
            timestamp=datetime.now(),
            window_title=f"Test_{i}"
        )
        learning_manager.observe(action)
    
    all_tasks = learning_manager.get_all_tasks()
    
    assert len(all_tasks) == 3
    assert all(isinstance(task, dict) for task in all_tasks)


def test_get_task_stats(learning_manager):
    """Test les statistiques globales."""
    # Créer des tâches dans différents modes
    for i in range(3):
        action = Action(
            action_type="click",
            target_element=f"button_{i}",
            bbox=(100, 100, 50, 30),
            confidence=0.9,
            embedding=np.random.rand(512).astype(np.float32),
            timestamp=datetime.now(),
            window_title=f"Test_{i}"
        )
        learning_manager.observe(action)
    
    stats = learning_manager.get_task_stats()
    
    assert "total_tasks" in stats
    assert "shadow_tasks" in stats
    assert "assist_tasks" in stats
    assert "auto_tasks" in stats
    assert stats["total_tasks"] == 3


def test_record_execution(learning_manager):
    """Test l'enregistrement d'une exécution."""
    action = Action(
        action_type="click",
        target_element="button",
        bbox=(100, 100, 50, 30),
        confidence=0.9,
        embedding=np.random.rand(512).astype(np.float32),
        timestamp=datetime.now(),
        window_title="Test"
    )
    
    learning_manager.observe(action)
    task_id = list(learning_manager.tasks.keys())[0]
    
    # Enregistrer une exécution
    learning_manager.record_execution({
        "task_id": task_id,
        "confidence": 0.92
    })
    
    task = learning_manager.tasks[task_id]
    assert task.confidence_score == 0.92
    assert task.last_execution is not None


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
