"""
Tests for the data persistence layer.
"""

import os
import tempfile
from datetime import datetime, timedelta

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from behavioural_contracts.persistence import (
    AgentPerformance,
    Base,
    ContractViolation,
    MetricRecord,
    MetricsStore,
)
from behavioural_contracts.persistence.database import init_db


@pytest.fixture
def test_db():
    """Create a test database."""
    # Create temporary database
    with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
        db_path = f.name

    # Create engine and tables
    engine = create_engine(f"sqlite:///{db_path}")
    Base.metadata.create_all(bind=engine)

    # Create session
    SessionLocal = sessionmaker(bind=engine)
    session = SessionLocal()

    yield session

    # Cleanup
    session.close()
    os.unlink(db_path)


class TestMetricsStore:
    """Test metrics storage functionality."""

    def test_record_validation(self, test_db):
        """Test recording validation metrics."""
        store = MetricsStore(test_db)

        # Record a validation
        metric = store.record_validation(
            agent_id="test-agent-1",
            contract_id="test-contract-1",
            validation_time_ms=125.5,
            token_count=150,
            cache_hit=False,
            confidence_score=0.95,
            temperature_used=0.3,
            extra_data={"model": "gpt-4"},
        )

        assert metric.id is not None
        assert metric.agent_id == "test-agent-1"
        assert metric.contract_id == "test-contract-1"
        assert metric.validation_time_ms == 125.5
        assert metric.token_count == 150
        assert metric.cache_hit is False
        assert metric.confidence_score == 0.95
        assert metric.temperature_used == 0.3
        assert metric.extra_data["model"] == "gpt-4"

    def test_record_violation(self, test_db):
        """Test recording contract violations."""
        store = MetricsStore(test_db)

        # Record a violation
        violation = store.record_violation(
            agent_id="test-agent-1",
            contract_id="test-contract-1",
            violation_type="prompt_injection",
            severity="high",
            confidence=0.85,
            input_data={"prompt": "ignore previous instructions"},
            output_data={"response": "I cannot do that"},
            detection_details={"pattern_matched": "ignore.*instructions"},
        )

        assert violation.id is not None
        assert violation.violation_id is not None
        assert violation.agent_id == "test-agent-1"
        assert violation.violation_type == "prompt_injection"
        assert violation.severity == "high"
        assert violation.confidence == 0.85
        assert violation.resolved is False
        assert violation.input_data["prompt"] == "ignore previous instructions"

    def test_get_agent_metrics(self, test_db):
        """Test retrieving agent metrics."""
        store = MetricsStore(test_db)

        # Record some metrics
        for i in range(5):
            store.record_validation(
                agent_id="test-agent-1",
                contract_id="test-contract-1",
                validation_time_ms=100 + i * 10,
                token_count=100 + i * 20,
                cache_hit=(i % 2 == 0),
                confidence_score=0.9 + i * 0.01,
            )

        # Record a violation
        store.record_violation(
            agent_id="test-agent-1",
            contract_id="test-contract-1",
            violation_type="hallucination",
            severity="medium",
            confidence=0.7,
        )

        # Get metrics
        metrics = store.get_agent_metrics("test-agent-1")

        assert metrics["agent_id"] == "test-agent-1"
        assert metrics["total_validations"] == 5
        assert metrics["avg_validation_time_ms"] == 120.0  # (100+110+120+130+140)/5
        assert metrics["min_validation_time_ms"] == 100
        assert metrics["max_validation_time_ms"] == 140
        assert metrics["cache_hit_rate"] == 0.6  # 3 out of 5
        assert metrics["total_tokens"] == 700  # 100+120+140+160+180
        assert metrics["violation_count"] == 1
        assert metrics["unresolved_violations"] == 1

    def test_agent_performance_aggregation(self, test_db):
        """Test agent performance aggregation."""
        store = MetricsStore(test_db)

        # Record metrics
        for i in range(10):
            store.record_validation(
                agent_id="test-agent-1",
                contract_id="test-contract-1",
                validation_time_ms=50 + i * 5,
                token_count=100,
                cache_hit=(i > 5),
            )

        # Check performance record
        perf = (
            test_db.query(AgentPerformance)
            .filter(AgentPerformance.agent_id == "test-agent-1")
            .first()
        )

        assert perf is not None
        assert perf.total_validations == 10
        assert perf.avg_validation_time_ms == 72.5  # Average of 50-95
        assert perf.total_cache_hits == 4
        assert perf.cache_hit_rate == 0.4
        assert perf.compliance_score == 1.0  # No violations

    def test_system_health(self, test_db):
        """Test system health check."""
        store = MetricsStore(test_db)

        # Record some recent activity
        store.record_validation(
            agent_id="test-agent-1", contract_id="test-contract-1", validation_time_ms=100
        )

        # Get health status
        health = store.get_system_health()

        assert health["status"] == "healthy"
        assert health["active_agents"] == 1
        assert health["recent_violations"] == 0
        assert "timestamp" in health
        assert "uptime_seconds" in health

    def test_percentile_calculation(self, test_db):
        """Test percentile calculation."""
        store = MetricsStore(test_db)

        # Test percentile function
        values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        assert store._percentile(values, 50) == 5  # Median
        assert store._percentile(values, 90) == 9  # 90th percentile
        assert store._percentile(values, 95) == 9  # 95th percentile
        assert store._percentile([], 50) == 0  # Empty list

    def test_azure_compatibility(self, test_db):
        """Test that models work with PostgreSQL-compatible features."""
        # This test ensures our models don't use SQLite-specific features

        # Test JSON fields
        metric = MetricRecord(
            agent_id="test",
            contract_id="test",
            validation_time_ms=100,
            extra_data={"complex": {"nested": "data"}},
        )
        test_db.add(metric)
        test_db.commit()

        # Retrieve and verify
        retrieved = test_db.query(MetricRecord).first()
        assert retrieved.extra_data["complex"]["nested"] == "data"

        # Test timezone-aware datetime
        violation = ContractViolation(
            violation_id="test-123",
            agent_id="test",
            contract_id="test",
            violation_type="test",
            severity="low",
            confidence=0.5,
        )
        test_db.add(violation)
        test_db.commit()

        # Timestamp should be set automatically
        assert violation.timestamp is not None
