"""
— SDK Response Models
============================================
Typed, immutable response objects that wrap the raw JSON from the NEES GP API.
All fields map 1-to-1 with the API schema so nothing is silently dropped.

The governance metadata fields (intent, framework, urgency, governance_clean)
are the SDK's key differentiator — they expose the governance layer's reasoning
alongside the response text so callers can adapt their application behaviour.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Optional


# ─────────────────────────────────────────────────────────────────────────────
# Chat
# ─────────────────────────────────────────────────────────────────────────────

@dataclass(frozen=True)
class LatencyInfo:
    """Per-layer latency breakdown from the governance pipeline."""
    llm_ms:   float   # time spent in the LLM call
    total_ms: float   # full pipeline wall-clock time (policy + governance + LLM)

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> "LatencyInfo":
        return cls(
            llm_ms=float(raw.get("llm_latency_ms",   0.0)),
            total_ms=float(raw.get("total_latency_ms", 0.0)),
        )

    def __repr__(self) -> str:
        return f"LatencyInfo(llm={self.llm_ms:.1f}ms total={self.total_ms:.1f}ms)"


@dataclass(frozen=True)
class ChatResponse:
    """
    Response from POST /v1/proxy/chat.

    Fields
    ──────
    text              The response text from Naina.
                      Ready to display directly to the user.

    intent            Classified intent of the user's message.
                      Values: confusion | emotional | decision | factual |
                              override | policy_blocked | error

    emotional_state   Detected emotional state of the user.
                      Values: neutral | stressed | lost | curious | distressed

    framework         The response structure pattern that was applied.
                      Values: confusion_direction | emotional_support |
                              decision_making | general_query | safe_hold

    urgency           Detected urgency level.
                      Values: low | medium | high

    governance_clean  True if the response passed all governance filters.
                      False means a violation was detected and a safe fallback
                      was returned instead.

    trace_id          Unique identifier for this request. Use this to
                      correlate with ledger entries via GET /ledger/{trace_id}.

    language_hint     Detected input language: hinglish | english | hindi.

    latency           Per-layer timing breakdown. Useful for monitoring.
    """
    text:             str
    intent:           str
    emotional_state:  str
    framework:        str
    urgency:          str
    governance_clean: bool
    trace_id:         str
    language_hint:    str
    latency:          LatencyInfo

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> "ChatResponse":
        return cls(
            text=raw.get("response", ""),
            intent=raw.get("intent_type", ""),
            emotional_state=raw.get("emotional_state", ""),
            framework=raw.get("framework_used", ""),
            urgency=raw.get("urgency_level", "low"),
            governance_clean=bool(raw.get("governance_clean", True)),
            trace_id=raw.get("trace_id", ""),
            language_hint=raw.get("language_hint", "english"),
            latency=LatencyInfo.from_dict(raw),
        )

    def __repr__(self) -> str:
        snippet = (self.text[:60] + "…") if len(self.text) > 60 else self.text
        return (
            f'ChatResponse(text="{snippet}", intent={self.intent!r}, '
            f'urgency={self.urgency!r}, clean={self.governance_clean})'
        )


# ─────────────────────────────────────────────────────────────────────────────
# Governance — proposal / pipeline objects
# ─────────────────────────────────────────────────────────────────────────────

@dataclass(frozen=True)
class AdmissibilityResult:
    """Nested admissibility verdict from a governance proposal."""
    verdict:          str
    reason:           str
    requires_commit:  bool
    rss_compliant:    bool
    gate_reached:     int

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> "AdmissibilityResult":
        return cls(
            verdict=raw.get("verdict", ""),
            reason=raw.get("reason", ""),
            requires_commit=bool(raw.get("requires_commit", False)),
            rss_compliant=bool(raw.get("rss_compliant", True)),
            gate_reached=int(raw.get("gate_reached", 0)),
        )


@dataclass(frozen=True)
class ProposalResponse:
    """Response from POST /propose."""
    action_id:     str
    name:          str
    category:      str
    risk_level:    str
    status:        str
    admissibility: AdmissibilityResult
    drift:         dict[str, Any]

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> "ProposalResponse":
        return cls(
            action_id=raw.get("action_id", ""),
            name=raw.get("name", ""),
            category=raw.get("category", ""),
            risk_level=raw.get("risk_level", ""),
            status=raw.get("status", ""),
            admissibility=AdmissibilityResult.from_dict(
                raw.get("admissibility", {})
            ),
            drift=raw.get("drift", {}),
        )


# ─────────────────────────────────────────────────────────────────────────────
# Drift / trajectory
# ─────────────────────────────────────────────────────────────────────────────

@dataclass(frozen=True)
class DriftReport:
    """
    Response from GET /trajectory.
    Summarises the current drift state of the governance pipeline session.
    """
    status:          str     # NOMINAL | WARNED | DRIFTING | ESCALATION | HALTED
    anomaly_score:   float   # 0.0–1.0 (>0.7 = ESCALATION)
    drift_magnitude: int     # raw count (>8 triggers escalation by default)
    is_halted:       bool
    trajectory_length: int

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> "DriftReport":
        return cls(
            status=raw.get("status", "NOMINAL"),
            anomaly_score=float(raw.get("anomaly_score", 0.0)),
            drift_magnitude=int(raw.get("drift_magnitude", 0)),
            is_halted=bool(raw.get("is_halted", False)),
            trajectory_length=int(raw.get("trajectory_length", 0)),
        )

    @property
    def is_safe(self) -> bool:
        """True when status is NOMINAL or WARNED (not yet blocking)."""
        return self.status in ("NOMINAL", "WARNED")

    def __repr__(self) -> str:
        return (
            f"DriftReport(status={self.status!r}, "
            f"anomaly={self.anomaly_score:.3f}, "
            f"magnitude={self.drift_magnitude})"
        )


# ─────────────────────────────────────────────────────────────────────────────
# Health
# ─────────────────────────────────────────────────────────────────────────────

@dataclass(frozen=True)
class HealthResponse:
    """Response from GET /health."""
    healthy:          bool
    uptime_seconds:   float
    session_id:       str
    authority_state:  str
    mode:             str
    ledger_entries:   int
    ledger_integrity: bool

    @classmethod
    def from_dict(cls, raw: dict[str, Any]) -> "HealthResponse":
        return cls(
            healthy=raw.get("status") == "healthy",
            uptime_seconds=float(raw.get("uptime_seconds", 0.0)),
            session_id=raw.get("session_id", ""),
            authority_state=raw.get("authority_state", ""),
            mode=raw.get("mode", ""),
            ledger_entries=int(raw.get("ledger_entries", 0)),
            ledger_integrity=bool(raw.get("ledger_integrity", True)),
        )
