"""
Metrics storage and retrieval operations.

Provides high-level interface for storing and querying metrics.
"""

import uuid
from datetime import datetime, timedelta
from typing import Any

from sqlalchemy import and_, func
from sqlalchemy.orm import Session

from .models import AgentPerformance, ContractViolation, MetricRecord


class MetricsStore:
    """
    Handle metrics storage and retrieval operations.
    """

    def __init__(self, session: Session):
        """Initialize with database session."""
        self.session = session

    def record_validation(
        self,
        agent_id: str,
        contract_id: str,
        validation_time_ms: float,
        token_count: int | None = None,
        cache_hit: bool = False,
        confidence_score: float | None = None,
        temperature_used: float | None = None,
        extra_data: dict[str, Any] | None = None,
    ) -> MetricRecord:
        """
        Record a contract validation metric.

        Args:
            agent_id: Unique identifier for the agent
            contract_id: Contract being validated
            validation_time_ms: Time taken in milliseconds
            token_count: Number of tokens used (if applicable)
            cache_hit: Whether result was from cache
            confidence_score: Confidence in the response
            temperature_used: Temperature parameter used
            extra_data: Additional context data

        Returns:
            Created metric record
        """
        metric = MetricRecord(
            agent_id=agent_id,
            contract_id=contract_id,
            validation_time_ms=validation_time_ms,
            token_count=token_count,
            cache_hit=cache_hit,
            confidence_score=confidence_score,
            temperature_used=temperature_used,
            extra_data=extra_data or {},
        )

        self.session.add(metric)
        self.session.commit()

        # Update agent performance asynchronously (in real app)
        self._update_agent_performance(agent_id)

        return metric

    def record_violation(
        self,
        agent_id: str,
        contract_id: str,
        violation_type: str,
        severity: str,
        confidence: float,
        input_data: dict[str, Any] | None = None,
        output_data: dict[str, Any] | None = None,
        detection_details: dict[str, Any] | None = None,
    ) -> ContractViolation:
        """
        Record a contract violation or security event.

        Args:
            agent_id: Agent that caused violation
            contract_id: Contract that was violated
            violation_type: Type of violation
            severity: Severity level (low/medium/high/critical)
            confidence: Detection confidence (0.0-1.0)
            input_data: Input that caused violation
            output_data: Output that violated contract
            detection_details: Specific detection information

        Returns:
            Created violation record
        """
        violation = ContractViolation(
            violation_id=str(uuid.uuid4()),
            agent_id=agent_id,
            contract_id=contract_id,
            violation_type=violation_type,
            severity=severity,
            confidence=confidence,
            input_data=input_data or {},
            output_data=output_data or {},
            detection_details=detection_details or {},
        )

        self.session.add(violation)
        self.session.commit()

        # Update agent performance
        self._update_agent_performance(agent_id)

        return violation

    def get_agent_metrics(
        self, agent_id: str, start_time: datetime | None = None, end_time: datetime | None = None
    ) -> dict[str, Any]:
        """
        Get metrics for a specific agent.

        Args:
            agent_id: Agent to get metrics for
            start_time: Start of time range (default: last 24h)
            end_time: End of time range (default: now)

        Returns:
            Dictionary of metrics
        """
        if not end_time:
            end_time = datetime.utcnow()
        if not start_time:
            start_time = end_time - timedelta(hours=24)

        # Get raw metrics
        metrics_query = self.session.query(MetricRecord).filter(
            and_(
                MetricRecord.agent_id == agent_id,
                MetricRecord.timestamp >= start_time,
                MetricRecord.timestamp <= end_time,
            )
        )

        metrics = metrics_query.all()

        if not metrics:
            return {
                "agent_id": agent_id,
                "period": {"start": start_time, "end": end_time},
                "total_validations": 0,
                "avg_validation_time_ms": 0,
                "cache_hit_rate": 0,
                "total_tokens": 0,
                "violations": [],
            }

        # Calculate aggregates
        total_validations = len(metrics)
        validation_times = [m.validation_time_ms for m in metrics]
        cache_hits = sum(1 for m in metrics if m.cache_hit)
        total_tokens = sum(m.token_count or 0 for m in metrics)

        # Get violations
        violations = (
            self.session.query(ContractViolation)
            .filter(
                and_(
                    ContractViolation.agent_id == agent_id,
                    ContractViolation.timestamp >= start_time,
                    ContractViolation.timestamp <= end_time,
                )
            )
            .all()
        )

        return {
            "agent_id": agent_id,
            "period": {"start": start_time, "end": end_time},
            "total_validations": total_validations,
            "avg_validation_time_ms": sum(validation_times) / len(validation_times),
            "min_validation_time_ms": min(validation_times),
            "max_validation_time_ms": max(validation_times),
            "p95_validation_time_ms": self._percentile(validation_times, 95),
            "cache_hit_rate": cache_hits / total_validations if total_validations > 0 else 0,
            "total_tokens": total_tokens,
            "avg_tokens_per_request": total_tokens / total_validations
            if total_validations > 0
            else 0,
            "violations": [
                {
                    "id": v.violation_id,
                    "type": v.violation_type,
                    "severity": v.severity,
                    "timestamp": v.timestamp,
                    "resolved": v.resolved,
                }
                for v in violations
            ],
            "violation_count": len(violations),
            "critical_violations": sum(1 for v in violations if v.severity == "critical"),
            "unresolved_violations": sum(1 for v in violations if not v.resolved),
        }

    def get_system_health(self) -> dict[str, Any]:
        """
        Get overall system health metrics.

        Returns:
            System health status and metrics
        """
        # Get global performance stats (simplified for now)
        global_stats = {"uptime_seconds": 0, "avg_validation_time_ms": 0, "total_validations": 0}

        # Get recent violations
        recent_violations = (
            self.session.query(ContractViolation)
            .filter(
                and_(
                    ContractViolation.timestamp >= datetime.utcnow() - timedelta(hours=1),
                    not ContractViolation.resolved,
                )
            )
            .count()
        )

        # Get active agents (validated in last hour)
        active_agents = (
            self.session.query(func.count(func.distinct(MetricRecord.agent_id)))
            .filter(MetricRecord.timestamp >= datetime.utcnow() - timedelta(hours=1))
            .scalar()
        )

        # Determine health status
        if recent_violations > 10 or global_stats.get("avg_validation_time_ms", 0) > 1000:
            status = "degraded"
        elif recent_violations > 20:
            status = "critical"
        else:
            status = "healthy"

        return {
            "status": status,
            "timestamp": datetime.utcnow(),
            "active_agents": active_agents,
            "recent_violations": recent_violations,
            "global_stats": global_stats,
            "uptime_seconds": global_stats.get("uptime_seconds", 0),
        }

    def _update_agent_performance(self, agent_id: str) -> None:
        """
        Update aggregated performance for an agent.

        In production, this would be done asynchronously.
        """
        # Get or create performance record
        perf = (
            self.session.query(AgentPerformance)
            .filter(AgentPerformance.agent_id == agent_id)
            .first()
        )

        if not perf:
            perf = AgentPerformance(agent_id=agent_id)
            self.session.add(perf)

        # Update aggregated metrics (simplified for now)
        metrics = self.session.query(MetricRecord).filter(MetricRecord.agent_id == agent_id).all()

        if metrics:
            perf.total_validations = len(metrics)
            validation_times = [m.validation_time_ms for m in metrics]
            perf.avg_validation_time_ms = sum(validation_times) / len(validation_times)
            perf.total_cache_hits = sum(1 for m in metrics if m.cache_hit)
            perf.cache_hit_rate = perf.total_cache_hits / perf.total_validations
            perf.total_tokens = sum(m.token_count or 0 for m in metrics)

        # Update violation counts
        violations = (
            self.session.query(ContractViolation)
            .filter(ContractViolation.agent_id == agent_id)
            .all()
        )

        perf.total_violations = len(violations)
        perf.critical_violations = sum(1 for v in violations if v.severity == "critical")
        perf.high_violations = sum(1 for v in violations if v.severity == "high")

        if violations:
            perf.last_violation_timestamp = max(v.timestamp for v in violations)

        # Calculate compliance score (simplified)
        if perf.total_validations > 0:
            perf.compliance_score = 1.0 - (perf.total_violations / perf.total_validations)
        else:
            perf.compliance_score = 1.0

        perf.last_updated = datetime.utcnow()
        self.session.commit()

    @staticmethod
    def _percentile(values: list[float], percentile: int) -> float:
        """Calculate percentile value."""
        if not values:
            return 0
        sorted_values = sorted(values)
        index = int(len(sorted_values) * (percentile / 100.0))
        if index > 0:
            index -= 1  # Adjust for 0-based indexing
        return sorted_values[min(index, len(sorted_values) - 1)]
