"""gRPC call analysis for issue detection.

Analyzes captured gRPC operations for errors, deadline exceeded,
slow calls, and connection issues.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, List

if TYPE_CHECKING:
    from autodebug.detection.transcript_analyzer import GRPCRecord, TranscriptInsight


# gRPC status codes
GRPC_STATUS_NAMES = {
    0: "OK",
    1: "CANCELLED",
    2: "UNKNOWN",
    3: "INVALID_ARGUMENT",
    4: "DEADLINE_EXCEEDED",
    5: "NOT_FOUND",
    6: "ALREADY_EXISTS",
    7: "PERMISSION_DENIED",
    8: "RESOURCE_EXHAUSTED",
    9: "FAILED_PRECONDITION",
    10: "ABORTED",
    11: "OUT_OF_RANGE",
    12: "UNIMPLEMENTED",
    13: "INTERNAL",
    14: "UNAVAILABLE",
    15: "DATA_LOSS",
    16: "UNAUTHENTICATED",
}


class GRPCAnalyzer:
    """Analyze gRPC call records for issues.

    Detects:
    - gRPC error status codes
    - Deadline exceeded
    - Unavailable / connection issues
    - Permission / auth errors
    - Slow calls
    """

    def __init__(self, slow_threshold_ms: float = 2000):
        self.slow_threshold_ms = slow_threshold_ms

    def analyze(self, records: List[GRPCRecord]) -> List[TranscriptInsight]:
        """Analyze gRPC records for issues."""

        insights: List[TranscriptInsight] = []

        for record in records:
            insights.extend(self._check_status(record))
            insights.extend(self._check_slow_call(record))
            insights.extend(self._check_metadata(record))

        return insights

    def _check_status(self, record: GRPCRecord) -> List[TranscriptInsight]:
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        if record.status_code == 0:
            return []

        # Validate status code range (0-16 are defined gRPC codes)
        if record.status_code not in range(0, 17):
            return [
                TranscriptInsight(
                    category="grpc_invalid_status",
                    severity=3,
                    description=f"Unknown gRPC status code: {record.status_code}",
                    evidence={"status_code": record.status_code},
                    related_records=[record.seq],
                    suggested_investigation="Check if gRPC client/server is using valid status codes",
                )
            ]

        status_name = GRPC_STATUS_NAMES.get(record.status_code, f"CODE_{record.status_code}")
        service_method = f"{record.service}/{record.method}" if record.service else record.method

        severity, category, suggestion = self._classify_status(record.status_code)

        return [
            TranscriptInsight(
                category=category,
                severity=severity,
                description=f"gRPC {status_name} from {service_method}",
                evidence={
                    "service": record.service,
                    "method": record.method,
                    "status_code": record.status_code,
                    "status_name": status_name,
                    "status_message": record.status_message[:500] if record.status_message else "",
                },
                related_records=[record.seq],
                suggested_investigation=suggestion,
            )
        ]

    def _classify_status(self, code: int) -> tuple:
        """Return (severity, category, suggestion) for a gRPC status code."""
        classifications = {
            1: (2, "grpc_cancelled", "Check if client cancelled the request intentionally"),
            2: (4, "grpc_unknown", "Check server logs for unhandled exceptions"),
            3: (3, "grpc_invalid_argument", "Validate request parameters before sending"),
            4: (5, "grpc_deadline_exceeded", "Increase deadline or optimize server-side processing"),
            5: (3, "grpc_not_found", "Check if the requested resource exists"),
            6: (3, "grpc_already_exists", "Handle duplicate resource creation gracefully"),
            7: (4, "grpc_permission_denied", "Check service account permissions"),
            8: (5, "grpc_resource_exhausted", "Service is overloaded - implement backoff/rate limiting"),
            9: (4, "grpc_failed_precondition", "System is not in required state for this operation"),
            10: (4, "grpc_aborted", "Transaction aborted - implement retry logic"),
            11: (3, "grpc_out_of_range", "Value is out of valid range - check input bounds"),
            12: (4, "grpc_unimplemented", "Method not implemented on server"),
            13: (5, "grpc_internal", "Server internal error - check server logs"),
            14: (5, "grpc_unavailable", "Service is unavailable - check connectivity and server health"),
            15: (5, "grpc_data_loss", "Unrecoverable data loss - critical error"),
            16: (4, "grpc_unauthenticated", "Check authentication credentials and token validity"),
        }
        return classifications.get(code, (4, "grpc_error", "Check gRPC server logs"))

    def _check_slow_call(self, record: GRPCRecord) -> List[TranscriptInsight]:
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        service_method = f"{record.service}/{record.method}" if record.service else record.method
        insights: List[TranscriptInsight] = []

        # Check against deadline if set
        deadline_ms = getattr(record, "deadline_ms", None)
        if deadline_ms and record.duration_ms > deadline_ms * 0.8:
            severity = 5 if record.duration_ms > deadline_ms else 4
            insights.append(
                TranscriptInsight(
                    category="grpc_deadline_risk",
                    severity=severity,
                    description=f"gRPC call to {service_method} at {record.duration_ms:.0f}ms / {deadline_ms:.0f}ms deadline",
                    evidence={
                        "service": record.service,
                        "method": record.method,
                        "duration_ms": record.duration_ms,
                        "deadline_ms": deadline_ms,
                    },
                    related_records=[record.seq],
                    suggested_investigation="Call is approaching or exceeding deadline - optimize or increase deadline",
                )
            )
            return insights

        if record.duration_ms < self.slow_threshold_ms:
            return []

        severity = 4 if record.duration_ms > 5000 else 3

        return [
            TranscriptInsight(
                category="slow_grpc",
                severity=severity,
                description=f"Slow gRPC call to {service_method} ({record.duration_ms:.0f}ms)",
                evidence={
                    "service": record.service,
                    "method": record.method,
                    "duration_ms": record.duration_ms,
                },
                related_records=[record.seq],
                suggested_investigation="Check server-side processing time or network latency",
            )
        ]

    def _check_metadata(self, record: GRPCRecord) -> List[TranscriptInsight]:
        """Surface error details stored in metadata/trailers."""
        from autodebug.detection.transcript_analyzer import TranscriptInsight

        if not record.metadata:
            return []

        lower_keys = {k.lower(): v for k, v in record.metadata.items()}
        detail = None
        if "grpc-status-details-bin" in lower_keys:
            detail = "grpc-status-details-bin"
        elif "error" in lower_keys:
            detail = "error"
        elif "exception" in lower_keys:
            detail = "exception"

        if not detail:
            return []

        raw_value = lower_keys.get(detail, "")
        if isinstance(raw_value, bytes):
            metadata_value = raw_value[:200].decode("utf-8", errors="replace")
        else:
            metadata_value = str(raw_value)[:200]

        return [
            TranscriptInsight(
                category="grpc_error_metadata",
                severity=3,
                description=f"gRPC metadata includes error detail ({detail})",
                evidence={
                    "service": record.service,
                    "method": record.method,
                    "metadata_key": detail,
                    "metadata_value": metadata_value,
                },
                related_records=[record.seq],
                suggested_investigation="Check server error details in gRPC trailers",
            )
        ]
