"""
Tests d'intégration pour la récupération d'erreurs

Teste les scénarios complets de gestion d'erreurs :
- Récupération après échec de matching
- Récupération après target introuvable avec fallbacks
- Récupération après violation de post-conditions
- Détection et gestion de changements UI
- Rollback d'actions
"""

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

from core.execution.error_handler import ErrorHandler, RecoveryStrategy
from core.execution.action_executor import ActionExecutor, ExecutionStatus
from core.execution.target_resolver import TargetResolver
from core.graph.node_matcher import NodeMatcher
from core.models.screen_state import ScreenState, RawLevel, PerceptionLevel
from core.models.workflow_graph import WorkflowNode, WorkflowEdge, Action, ActionType
from core.models.ui_element import UIElement
from core.embedding.state_embedding_builder import StateEmbeddingBuilder


@pytest.fixture
def temp_dirs():
    """Créer des répertoires temporaires pour les tests."""
    error_dir = tempfile.mkdtemp()
    failed_matches_dir = tempfile.mkdtemp()
    yield error_dir, failed_matches_dir
    shutil.rmtree(error_dir)
    shutil.rmtree(failed_matches_dir)


@pytest.fixture
def error_handler(temp_dirs):
    """Créer ErrorHandler pour les tests."""
    error_dir, _ = temp_dirs
    return ErrorHandler(error_log_dir=error_dir)


@pytest.fixture
def action_executor(error_handler):
    """Créer ActionExecutor avec ErrorHandler."""
    target_resolver = TargetResolver()
    return ActionExecutor(
        target_resolver=target_resolver,
        error_handler=error_handler,
        verify_postconditions=True
    )


@pytest.fixture
def node_matcher(error_handler, temp_dirs):
    """Créer NodeMatcher avec ErrorHandler."""
    _, failed_matches_dir = temp_dirs
    embedding_builder = StateEmbeddingBuilder()
    return NodeMatcher(
        embedding_builder=embedding_builder,
        error_handler=error_handler,
        failed_matches_dir=failed_matches_dir
    )


@pytest.fixture
def mock_screen_state_with_button():
    """Créer un ScreenState avec un bouton."""
    raw_level = RawLevel(
        window_title="Test App",
        screenshot_path=Path("/tmp/test.png"),
        timestamp=datetime.now()
    )
    
    button = UIElement(
        element_id="btn_1",
        role="button",
        text="Submit",
        bbox=(100, 100, 200, 150)
    )
    
    perception_level = PerceptionLevel(
        ui_elements=[button],
        timestamp=datetime.now()
    )
    
    return ScreenState(
        raw_level=raw_level,
        perception_level=perception_level
    )


class TestActionExecutorErrorRecovery:
    """Tests de récupération d'erreurs dans ActionExecutor."""
    
    def test_target_not_found_triggers_error_handler(
        self, action_executor
    ):
        """Test que target introuvable déclenche ErrorHandler."""
        # Créer un état sans le bouton attendu
        raw_level = RawLevel(
            window_title="Test App",
            screenshot_path=Path("/tmp/test.png"),
            timestamp=datetime.now()
        )
        perception_level = PerceptionLevel(ui_elements=[], timestamp=datetime.now())
        screen_state = ScreenState(raw_level=raw_level, perception_level=perception_level)
        
        action = Action(
            type=ActionType.MOUSE_CLICK,
            target=Mock(role="button", text_pattern="Submit")
        )
        edge = WorkflowEdge(from_node="node_1", to_node="node_2", action=action)
        
        result = action_executor.execute_edge(edge=edge, screen_state=screen_state)
        
        assert result.status == ExecutionStatus.TARGET_NOT_FOUND
        stats = action_executor.get_error_statistics()
        assert stats['total_errors'] >= 1


class TestNodeMatcherErrorRecovery:
    """Tests de récupération d'erreurs dans NodeMatcher."""
    
    def test_matching_failure_triggers_error_handler(
        self, node_matcher, mock_screen_state_with_button
    ):
        """Test que l'échec de matching déclenche ErrorHandler."""
        node = WorkflowNode(node_id="node_1", label="Different State")
        node.matches = Mock(return_value=(False, 0.50))
        
        result = node_matcher.match(
            current_state=mock_screen_state_with_button,
            candidate_nodes=[node]
        )
        
        assert result is None
        stats = node_matcher.get_error_statistics()
        assert stats['total_errors'] >= 1


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