"""Transcript data analysis for intelligent issue detection.

Analyzes captured I/O data (HTTP, SQL, etc.) to detect anomalies,
errors, and patterns that may be related to failures.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Dict, List, Optional

if TYPE_CHECKING:
    from autodebug.detection.correlation_analyzer import CorrelationAnalyzer
    from autodebug.detection.grpc_analyzer import GRPCAnalyzer
    from autodebug.detection.http_analyzer import HTTPAnalyzer
    from autodebug.detection.redis_analyzer import RedisAnalyzer
    from autodebug.detection.security_analyzer import SecurityAnalyzer
    from autodebug.detection.sql_analyzer import SQLAnalyzer
    from autodebug.detection.timeout_predictor import TimeoutPredictor
    from autodebug.detection.timing_analyzer import TimingAnalyzer


@dataclass
class TranscriptInsight:
    """An insight discovered from transcript analysis.

    Represents a potential issue or pattern found in the I/O data
    that may be related to a failure.
    """

    category: str  # "http_error", "slow_query", "data_mismatch", etc.
    severity: int  # 1-5 (5 is most severe)
    description: str
    evidence: Dict[str, Any] = field(default_factory=dict)
    related_records: List[int] = field(default_factory=list)  # Sequence numbers
    suggested_investigation: str = ""
    confidence: float = 1.0

    def __str__(self) -> str:
        return f"[{self.category}] (severity {self.severity}): {self.description}"


@dataclass
class HTTPRecord:
    """Represents an HTTP request/response record."""

    seq: int = 0
    method: str = "GET"
    url_canonical: str = ""
    status: int = 200
    request_body: Optional[str] = None
    response_body: Optional[str] = None
    headers: Dict[str, str] = field(default_factory=dict)
    duration_ms: float = 0.0


@dataclass
class SQLRecord:
    """Represents a SQL query record."""

    seq: int = 0
    sql_canonical: str = ""
    params: Optional[Dict[str, Any]] = None
    rows: Optional[List[Any]] = None
    row_count: int = 0
    duration_ms: float = 0.0
    exception: Optional[Dict[str, Any]] = None


@dataclass
class RedisRecord:
    """Represents a Redis command record."""

    seq: int = 0
    command: str = ""
    key: str = ""
    value: Optional[str] = None
    result: Optional[str] = None
    error: Optional[str] = None
    duration_ms: float = 0.0


@dataclass
class GRPCRecord:
    """Represents a gRPC call record."""

    seq: int = 0
    service: str = ""
    method: str = ""
    status_code: int = 0
    status_message: str = ""
    request_body: Optional[str] = None
    response_body: Optional[str] = None
    duration_ms: float = 0.0
    metadata: Optional[Dict[str, str]] = None
    deadline_ms: Optional[float] = None


@dataclass
class ExceptionRecord:
    """Represents an exception that occurred."""

    type: str = ""
    message: str = ""
    timestamp: float = 0.0
    stack_trace: Optional[List[str]] = None


@dataclass
class Transcript:
    """Complete transcript of a test/operation."""

    http_records: List[HTTPRecord] = field(default_factory=list)
    sql_records: List[SQLRecord] = field(default_factory=list)
    redis_records: List[RedisRecord] = field(default_factory=list)
    grpc_records: List[GRPCRecord] = field(default_factory=list)
    exception: Optional[ExceptionRecord] = None

    @property
    def all_records(self) -> List:
        """Get all records sorted by sequence."""
        all_recs = []
        all_recs.extend(self.http_records)
        all_recs.extend(self.sql_records)
        all_recs.extend(self.redis_records)
        all_recs.extend(self.grpc_records)
        return sorted(all_recs, key=lambda r: r.seq)


class TranscriptAnalyzer:
    """Main analyzer for transcript data.

    Coordinates multiple sub-analyzers (HTTP, SQL, correlation)
    to extract insights from captured I/O data.
    """

    def __init__(
        self,
        transcript: Optional[Transcript] = None,
        enable_timing_analysis: bool = True,
        enable_security_analysis: bool = True,
    ):
        """Initialize the transcript analyzer.

        Args:
            transcript: Optional transcript to analyze
            enable_timing_analysis: Whether to include timing analysis (Plan 58)
            enable_security_analysis: Whether to include security analysis
        """
        self.transcript = transcript
        self.enable_timing_analysis = enable_timing_analysis
        self.enable_security_analysis = enable_security_analysis
        self._http_analyzer = None
        self._sql_analyzer = None
        self._redis_analyzer = None
        self._grpc_analyzer = None
        self._correlation_analyzer = None
        self._security_analyzer = None
        self._timing_analyzer = None
        self._timeout_predictor = None

    @property
    def http_analyzer(self) -> HTTPAnalyzer:
        """Get or create HTTP analyzer."""
        if self._http_analyzer is None:
            from autodebug.detection.http_analyzer import HTTPAnalyzer
            self._http_analyzer = HTTPAnalyzer()
        return self._http_analyzer

    @property
    def sql_analyzer(self) -> SQLAnalyzer:
        """Get or create SQL analyzer."""
        if self._sql_analyzer is None:
            from autodebug.detection.sql_analyzer import SQLAnalyzer
            self._sql_analyzer = SQLAnalyzer()
        return self._sql_analyzer

    @property
    def redis_analyzer(self) -> RedisAnalyzer:
        """Get or create Redis analyzer."""
        if self._redis_analyzer is None:
            from autodebug.detection.redis_analyzer import RedisAnalyzer
            self._redis_analyzer = RedisAnalyzer()
        return self._redis_analyzer

    @property
    def grpc_analyzer(self) -> GRPCAnalyzer:
        """Get or create gRPC analyzer."""
        if self._grpc_analyzer is None:
            from autodebug.detection.grpc_analyzer import GRPCAnalyzer
            self._grpc_analyzer = GRPCAnalyzer()
        return self._grpc_analyzer

    @property
    def security_analyzer(self) -> SecurityAnalyzer:
        """Get or create security analyzer."""
        if self._security_analyzer is None:
            from autodebug.detection.security_analyzer import SecurityAnalyzer
            self._security_analyzer = SecurityAnalyzer()
        return self._security_analyzer

    @property
    def correlation_analyzer(self) -> CorrelationAnalyzer:
        """Get or create correlation analyzer."""
        if self._correlation_analyzer is None:
            from autodebug.detection.correlation_analyzer import CorrelationAnalyzer
            self._correlation_analyzer = CorrelationAnalyzer()
        return self._correlation_analyzer

    @property
    def timing_analyzer(self) -> TimingAnalyzer:
        """Get or create timing analyzer (Plan 58)."""
        if self._timing_analyzer is None:
            from autodebug.detection.timing_analyzer import TimingAnalyzer
            self._timing_analyzer = TimingAnalyzer()
        return self._timing_analyzer

    @property
    def timeout_predictor(self) -> TimeoutPredictor:
        """Get or create timeout predictor (Plan 58)."""
        if self._timeout_predictor is None:
            from autodebug.detection.timeout_predictor import TimeoutPredictor
            self._timeout_predictor = TimeoutPredictor()
        return self._timeout_predictor

    def analyze(self, transcript: Optional[Transcript] = None) -> List[TranscriptInsight]:
        """Run all analyzers and combine insights.

        Args:
            transcript: Transcript to analyze (uses self.transcript if not provided)

        Returns:
            List of insights sorted by severity
        """
        transcript = transcript or self.transcript
        if not transcript:
            return []

        insights: List[TranscriptInsight] = []

        # Analyze HTTP records
        if transcript.http_records:
            insights.extend(self.http_analyzer.analyze(transcript.http_records))

        # Analyze SQL records
        if transcript.sql_records:
            insights.extend(self.sql_analyzer.analyze(transcript.sql_records))

        # Analyze Redis records
        if transcript.redis_records:
            insights.extend(self.redis_analyzer.analyze(transcript.redis_records))

        # Analyze gRPC records
        if transcript.grpc_records:
            insights.extend(self.grpc_analyzer.analyze(transcript.grpc_records))

        # Security analysis
        if self.enable_security_analysis:
            insights.extend(self.security_analyzer.analyze(transcript))

        # Correlate with exception
        if transcript.exception:
            insights.extend(
                self.correlation_analyzer.correlate(
                    transcript.exception,
                    transcript.all_records
                )
            )

        # Timing analysis (Plan 58)
        if self.enable_timing_analysis:
            insights.extend(self._analyze_timing(transcript))

        return self._dedupe_and_rank(insights)

    def _analyze_timing(self, transcript: Transcript) -> List[TranscriptInsight]:
        """Run timing analysis and convert to insights (Plan 58).

        Args:
            transcript: Transcript to analyze

        Returns:
            List of timing-related insights
        """
        insights: List[TranscriptInsight] = []

        # Get timing issues
        timing_issues = self.timing_analyzer.analyze(transcript)
        for issue in timing_issues:
            insights.append(
                TranscriptInsight(
                    category=issue.category,
                    severity=issue.severity,
                    description=issue.description,
                    evidence=issue.evidence,
                    suggested_investigation=issue.suggestion,
                )
            )

        # Get timeout predictions
        prediction = self.timeout_predictor.predict(transcript)
        for risk in prediction.risks:
            # Map risk level to severity
            severity_map = {
                "critical": 5,
                "high": 4,
                "medium": 3,
                "low": 2,
            }
            severity = severity_map.get(risk.risk_level.value, 3)

            insights.append(
                TranscriptInsight(
                    category="timeout_risk",
                    severity=severity,
                    description=str(risk),
                    evidence={
                        "operation_type": risk.operation_type,
                        "duration_ms": risk.duration_ms,
                        "timeout_ms": risk.timeout_ms,
                        "headroom_ms": risk.headroom_ms,
                        **risk.evidence,
                    },
                    suggested_investigation=risk.recommendation,
                )
            )

        return insights

    def _dedupe_and_rank(
        self, insights: List[TranscriptInsight], max_insights: int = 500
    ) -> List[TranscriptInsight]:
        """Remove duplicates, clamp severity, and sort by severity.

        Args:
            insights: List of insights to process
            max_insights: Maximum number of insights to return

        Returns:
            Deduplicated and sorted insights
        """
        def _key(insight: TranscriptInsight) -> tuple:
            evidence = insight.evidence or {}
            url = (
                evidence.get("url")
                or evidence.get("http_url")
                or evidence.get("grpc_service")
                or ""
            )
            query = evidence.get("query") or evidence.get("sql") or ""
            return (insight.category, insight.description, url, query)

        seen = set()
        unique: List[TranscriptInsight] = []

        for insight in insights:
            # Clamp severity to valid range
            insight.severity = max(1, min(5, insight.severity))
            insight.confidence = max(0.0, min(1.0, insight.confidence))
            if len(insight.related_records) > 50:
                insight.related_records = insight.related_records[-50:]
            # Create a key for deduplication
            key = _key(insight)
            if key not in seen:
                seen.add(key)
                unique.append(insight)

        # Sort by severity (highest first) and limit
        return sorted(
            unique,
            key=lambda x: (x.severity * x.confidence, x.severity),
            reverse=True,
        )[:max_insights]

    def format_report(self, insights: Optional[List[TranscriptInsight]] = None) -> str:
        """Format insights as a human-readable report.

        Args:
            insights: List of insights (runs analysis if not provided)

        Returns:
            Formatted report string
        """
        if insights is None:
            insights = self.analyze()

        if not insights:
            return "No issues detected in transcript."

        lines = [
            "",
            "Transcript Analysis Report",
            "=" * 50,
            "",
        ]

        # Group by category
        by_category: Dict[str, List[TranscriptInsight]] = {}
        for insight in insights:
            if insight.category not in by_category:
                by_category[insight.category] = []
            by_category[insight.category].append(insight)

        for category, cat_insights in by_category.items():
            lines.append(f"## {category.replace('_', ' ').title()}")
            lines.append("")

            for insight in cat_insights:
                severity_marker = "!" * insight.severity
                lines.append(f"[{severity_marker}] {insight.description}")
                if insight.suggested_investigation:
                    lines.append(f"    Suggestion: {insight.suggested_investigation}")
                lines.append("")

        return "\n".join(lines)


def analyze_transcript(transcript: Transcript) -> List[TranscriptInsight]:
    """Convenience function to analyze a transcript.

    Args:
        transcript: Transcript to analyze

    Returns:
        List of insights
    """
    analyzer = TranscriptAnalyzer(transcript)
    return analyzer.analyze()
