"""
Tests for agentic AI instrumentation with MCA SDK.

This module tests goal tracking, tool monitoring, reasoning steps,
and human intervention features for autonomous agents.
"""

import pytest
import time
from unittest.mock import Mock, patch
from mca_sdk import MCAClient
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter


@pytest.fixture
def agentic_client():
    """Create MCAClient with in-memory exporters for testing."""
    # Create in-memory metric reader
    metric_reader = InMemoryMetricReader()
    meter_provider = MeterProvider(metric_readers=[metric_reader])

    # Create in-memory span exporter
    span_exporter = InMemorySpanExporter()
    tracer_provider = TracerProvider()
    tracer_provider.add_span_processor(SimpleSpanProcessor(span_exporter))

    # Create client with test providers
    client = MCAClient(
        service_name="test-agentic-agent",
        model_id="agent-test-001",
        model_version="1.0.0",
        team_name="test-team",
        collector_endpoint="http://localhost:4318",
        attributes={"model.category": "agentic"},
    )

    # Inject test providers
    client._meter_provider = meter_provider
    client._tracer_provider = tracer_provider
    client._metric_reader = metric_reader
    client._span_exporter = span_exporter

    yield client

    client.shutdown()


class TestGoalTracking:
    """Test goal lifecycle tracking."""

    def test_record_goal_started(self, agentic_client):
        """Test that goal start is recorded with unique ID."""
        goal_id = agentic_client.record_goal_started(
            goal_description="Test goal: analyze patient data"
        )

        assert goal_id is not None
        assert goal_id.startswith("goal-")
        assert len(goal_id) > 10  # Has timestamp and random component

    def test_record_goal_completed_success(self, agentic_client):
        """Test successful goal completion tracking."""
        goal_id = agentic_client.record_goal_started("Test goal")

        # Complete the goal
        agentic_client.record_goal_completed(
            goal_id=goal_id,
            status="success",
            result_summary="Goal completed successfully",
        )

        # Verify metrics were recorded
        metrics = agentic_client._metric_reader.get_metrics_data()
        metric_names = [
            metric.name
            for resource_metrics in metrics.resource_metrics
            for scope_metrics in resource_metrics.scope_metrics
            for metric in scope_metrics.metrics
        ]

        assert "agent.goals_started_total" in metric_names
        assert "agent.goals_completed_total" in metric_names

    def test_record_goal_completed_failure(self, agentic_client):
        """Test failed goal completion tracking."""
        goal_id = agentic_client.record_goal_started("Test goal")

        agentic_client.record_goal_completed(
            goal_id=goal_id,
            status="failure",
            result_summary="Goal failed due to insufficient data",
        )

        # Verify failure status was recorded
        metrics = agentic_client._metric_reader.get_metrics_data()
        # Check that completed metric includes status label
        assert any(
            metric.name == "agent.goals_completed_total"
            for resource_metrics in metrics.resource_metrics
            for scope_metrics in resource_metrics.scope_metrics
            for metric in scope_metrics.metrics
        )


class TestToolTracking:
    """Test tool execution monitoring."""

    def test_track_tool_context_manager(self, agentic_client):
        """Test tool tracking with context manager."""
        # Track tool execution
        with agentic_client.track_tool("pubmed_search") as tool:
            # Simulate tool work
            time.sleep(0.01)
            results = ["paper1", "paper2"]
            tool.set_attribute("results_count", len(results))

        # Verify span was created
        spans = agentic_client._span_exporter.get_finished_spans()
        assert len(spans) > 0

        tool_span = next((s for s in spans if "tool_execution" in s.name), None)
        assert tool_span is not None
        assert tool_span.attributes.get("tool.name") == "pubmed_search"
        assert tool_span.attributes.get("results_count") == 2

    def test_track_tool_with_error(self, agentic_client):
        """Test tool tracking when tool raises exception."""
        with pytest.raises(ValueError):
            with agentic_client.track_tool("failing_tool"):
                raise ValueError("Tool execution failed")

        # Verify span was created with error status
        spans = agentic_client._span_exporter.get_finished_spans()
        tool_span = next((s for s in spans if "tool_execution" in s.name), None)

        assert tool_span is not None
        assert tool_span.status.is_ok is False

    def test_tool_latency_metric(self, agentic_client):
        """Test that tool latency metric is recorded."""
        with agentic_client.track_tool("test_tool"):
            time.sleep(0.05)  # Simulate 50ms work

        # Verify latency metric exists
        metrics = agentic_client._metric_reader.get_metrics_data()
        metric_names = [
            metric.name
            for resource_metrics in metrics.resource_metrics
            for scope_metrics in resource_metrics.scope_metrics
            for metric in scope_metrics.metrics
        ]

        assert "agent.tool_latency_seconds" in metric_names


class TestReasoningSteps:
    """Test reasoning step instrumentation."""

    def test_reasoning_span_creation(self, agentic_client):
        """Test that reasoning spans are created correctly."""
        with agentic_client.tracer.start_as_current_span("agent.reasoning") as span:
            span.set_attribute("step_type", "analysis")
            span.set_attribute("papers_analyzed", 5)

        # Verify span was created
        spans = agentic_client._span_exporter.get_finished_spans()
        reasoning_span = next((s for s in spans if "reasoning" in s.name), None)

        assert reasoning_span is not None
        assert reasoning_span.attributes.get("step_type") == "analysis"
        assert reasoning_span.attributes.get("papers_analyzed") == 5

    def test_nested_reasoning_spans(self, agentic_client):
        """Test nested reasoning spans for multi-step thought process."""
        with agentic_client.tracer.start_as_current_span("agent.reasoning") as parent:
            parent.set_attribute("step_type", "planning")

            with agentic_client.tracer.start_as_current_span("agent.reasoning.analyze") as child:
                child.set_attribute("step_type", "analysis")

        # Verify parent-child relationship
        spans = agentic_client._span_exporter.get_finished_spans()
        assert len(spans) >= 2

        # Check that child span has parent context
        child_span = next((s for s in spans if "analyze" in s.name), None)
        parent_span = next(
            (s for s in spans if "reasoning" in s.name and "analyze" not in s.name),
            None,
        )

        assert child_span is not None
        assert parent_span is not None
        assert child_span.parent.span_id == parent_span.context.span_id


class TestHumanIntervention:
    """Test human intervention tracking."""

    def test_record_human_intervention(self, agentic_client):
        """Test recording human intervention events."""
        agentic_client.record_human_intervention(
            reason="clinical_review_required",
            action_taken="awaiting_approval",
            additional_context={"goal_id": "goal-123"},
        )

        # Verify intervention metric was recorded
        metrics = agentic_client._metric_reader.get_metrics_data()
        metric_names = [
            metric.name
            for resource_metrics in metrics.resource_metrics
            for scope_metrics in resource_metrics.scope_metrics
            for metric in scope_metrics.metrics
        ]

        assert "agent.human_interventions_total" in metric_names

    def test_intervention_reasons(self, agentic_client):
        """Test different intervention reason tracking."""
        reasons = [
            "clinical_review_required",
            "low_confidence",
            "ambiguous_query",
            "safety_check",
        ]

        for reason in reasons:
            agentic_client.record_human_intervention(reason=reason, action_taken="pending_review")

        # Verify all interventions were recorded
        metrics = agentic_client._metric_reader.get_metrics_data()
        assert any(
            metric.name == "agent.human_interventions_total"
            for resource_metrics in metrics.resource_metrics
            for scope_metrics in resource_metrics.scope_metrics
            for metric in scope_metrics.metrics
        )


class TestEndToEndAgentWorkflow:
    """Test complete agent workflow."""

    def test_full_agent_execution(self, agentic_client):
        """Test complete agent workflow: goal → tools → reasoning → completion."""
        # Start goal
        goal_id = agentic_client.record_goal_started("Find latest diabetes treatments")

        # Execute tool
        with agentic_client.track_tool("pubmed_search") as tool:
            results = ["paper1", "paper2"]
            tool.set_attribute("results_count", len(results))

        # Reasoning step
        with agentic_client.tracer.start_as_current_span("agent.reasoning") as span:
            span.set_attribute("step_type", "analysis")

        # Request human review
        agentic_client.record_human_intervention(
            reason="clinical_review_required", action_taken="awaiting_approval"
        )

        # Complete goal
        agentic_client.record_goal_completed(goal_id=goal_id, status="success")

        # Verify all telemetry was generated
        spans = agentic_client._span_exporter.get_finished_spans()
        assert len(spans) >= 2  # Tool + reasoning spans

        metrics = agentic_client._metric_reader.get_metrics_data()
        metric_names = [
            metric.name
            for resource_metrics in metrics.resource_metrics
            for scope_metrics in resource_metrics.scope_metrics
            for metric in scope_metrics.metrics
        ]

        # Check all expected metrics exist
        expected_metrics = [
            "agent.goals_started_total",
            "agent.goals_completed_total",
            "agent.tool_calls_total",
            "agent.human_interventions_total",
        ]

        for expected in expected_metrics:
            assert expected in metric_names, f"Missing metric: {expected}"
