"""CI-1T SDK response types.

Typed dataclasses for all API responses. Raw Q0.16 values (0-65535) are
converted to normalized floats (0.0-1.0) for convenience.
"""

from __future__ import annotations

from dataclasses import dataclass, field

# Q0.16 range
Q16_MAX = 65535

# CI stability thresholds (normalized 0.0-1.0)
CI_STABLE = 0.15
CI_DRIFT = 0.45
CI_FLIP = 0.70

# Authority level descriptions
AL_LABELS: dict[int, str] = {
    0: "Full trust",
    1: "High trust",
    2: "Moderate trust",
    3: "Low trust",
    4: "No authority",
    7: "Fault",
}


def _ci_status(ci: float) -> str:
    """Classify CI score into a human-readable status."""
    if ci <= CI_STABLE:
        return "Stable"
    if ci <= CI_DRIFT:
        return "Drifting"
    if ci <= CI_FLIP:
        return "Unstable"
    return "Collapsing"


def _to_float(raw: int) -> float:
    """Convert Q0.16 integer to float 0.0-1.0."""
    return raw / Q16_MAX


def to_q16(value: float | int) -> int:
    """Convert a float (0.0-1.0) or int (0-65535) to Q0.16 integer.

    Accepts:
        - float in [0.0, 1.0] -> scaled to 0-65535
        - int in [0, 65535] -> passed through
    """
    if isinstance(value, float):
        if not (0.0 <= value <= 1.0):
            raise ValueError(f"Float score must be 0.0-1.0, got {value}")
        return round(value * Q16_MAX)
    if isinstance(value, int):
        if not (0 <= value <= Q16_MAX):
            raise ValueError(f"Int score must be 0-{Q16_MAX}, got {value}")
        return value
    raise TypeError(f"Score must be float or int, got {type(value).__name__}")


# ── Single evaluate types ──


@dataclass(frozen=True, slots=True)
class Episode:
    """One episode of stability results."""

    ci_raw: int
    ci_ema_raw: int
    al: int
    warn: bool
    fault: bool
    fault_code: int
    ep_count: int
    ghost_suspect: bool
    ghost_confirmed: bool

    @property
    def ci(self) -> float:
        """Collapse Index as float 0.0-1.0."""
        return _to_float(self.ci_raw)

    @property
    def ci_ema(self) -> float:
        """Smoothed CI (EMA) as float 0.0-1.0."""
        return _to_float(self.ci_ema_raw)

    @property
    def stable(self) -> bool:
        """True if CI <= stable threshold (0.15)."""
        return self.ci <= CI_STABLE

    @property
    def status(self) -> str:
        """Human-readable status: Stable, Drifting, Unstable, or Collapsing."""
        return _ci_status(self.ci)

    @property
    def al_label(self) -> str:
        """Human-readable authority level."""
        return AL_LABELS.get(self.al, f"AL{self.al}")

    @classmethod
    def from_dict(cls, d: dict) -> Episode:
        """Parse an episode from API JSON."""
        return cls(
            ci_raw=d.get("ci_out", 0),
            ci_ema_raw=d.get("ci_ema_out", 0),
            al=d.get("al_out", 0),
            warn=d.get("warn", False),
            fault=d.get("fault", False),
            fault_code=d.get("fault_code", 0),
            ep_count=d.get("ep_count", 0),
            ghost_suspect=d.get("ghost_suspect", False),
            ghost_confirmed=d.get("ghost_confirmed", False),
        )


@dataclass(frozen=True, slots=True)
class EvaluateResult:
    """Response from /evaluate endpoint."""

    episodes: list[Episode]
    compute_ns: int
    episode_count: int
    credits_used: int = 0
    credits_remaining: int = 0

    @classmethod
    def from_dict(cls, d: dict) -> EvaluateResult:
        """Parse from API JSON."""
        return cls(
            episodes=[Episode.from_dict(ep) for ep in d.get("episodes", [])],
            compute_ns=d.get("compute_ns", 0),
            episode_count=d.get("episode_count", 0),
            credits_used=d.get("credits_used", 0),
            credits_remaining=d.get("credits_remaining", 0),
        )


# ── Fleet types ──


@dataclass(frozen=True, slots=True)
class FleetNode:
    """Per-node result from fleet evaluation."""

    ci_raw: int
    ci_ema_raw: int
    al: int
    warn: bool
    fault: bool
    ghost_suspect: bool
    ghost_confirmed: bool

    @property
    def ci(self) -> float:
        return _to_float(self.ci_raw)

    @property
    def ci_ema(self) -> float:
        return _to_float(self.ci_ema_raw)

    @property
    def stable(self) -> bool:
        return self.ci <= CI_STABLE

    @property
    def status(self) -> str:
        return _ci_status(self.ci)

    @property
    def al_label(self) -> str:
        return AL_LABELS.get(self.al, f"AL{self.al}")

    @classmethod
    def from_dict(cls, d: dict) -> FleetNode:
        """Parse from API JSON. Handles both flat and nested engine formats."""
        # Fleet responses may nest engine outputs under an "engine" key
        engine = d.get("engine", d)
        return cls(
            ci_raw=engine.get("ci_out", 0),
            ci_ema_raw=engine.get("ci_ema_out", 0),
            al=engine.get("al_out", 0),
            warn=engine.get("warn", False),
            fault=engine.get("fault", False),
            ghost_suspect=engine.get("ghost_suspect", False),
            ghost_confirmed=engine.get("ghost_confirmed", False),
        )


@dataclass(frozen=True, slots=True)
class FleetSnapshot:
    """Fleet evaluation snapshot with ghost detection."""

    nodes: list[FleetNode]
    active_node_count: int
    ghost_suspect_count: int
    ghost_confirmed_count: int

    @property
    def has_ghosts(self) -> bool:
        return self.ghost_confirmed_count > 0

    @property
    def has_suspects(self) -> bool:
        return self.ghost_suspect_count > 0

    @classmethod
    def from_dict(cls, d: dict) -> FleetSnapshot:
        """Parse from API JSON."""
        raw_nodes = d.get("nodes", d.get("node_results", []))
        return cls(
            nodes=[FleetNode.from_dict(n) for n in raw_nodes],
            active_node_count=d.get("active_node_count", len(raw_nodes)),
            ghost_suspect_count=d.get("ghost_suspect_count", 0),
            ghost_confirmed_count=d.get("ghost_confirmed_count", 0),
        )


@dataclass(frozen=True, slots=True)
class FleetEvaluateResult:
    """Response from /fleet-evaluate endpoint."""

    snapshot: FleetSnapshot
    compute_ns: int
    credits_used: int = 0
    credits_remaining: int = 0

    @classmethod
    def from_dict(cls, d: dict) -> FleetEvaluateResult:
        """Parse from API JSON."""
        return cls(
            snapshot=FleetSnapshot.from_dict(d.get("snapshot", d)),
            compute_ns=d.get("compute_ns", 0),
            credits_used=d.get("credits_used", 0),
            credits_remaining=d.get("credits_remaining", 0),
        )


# ── Session types ──


@dataclass(frozen=True, slots=True)
class SessionInfo:
    """Fleet session metadata."""

    session_id: str
    node_count: int
    node_names: list[str] = field(default_factory=list)
    round_count: int = 0

    @classmethod
    def from_dict(cls, d: dict) -> SessionInfo:
        return cls(
            session_id=d.get("session_id", ""),
            node_count=d.get("node_count", 0),
            node_names=d.get("node_names", []),
            round_count=d.get("round_count", 0),
        )


@dataclass(frozen=True, slots=True)
class RoundResult:
    """Response from pushing a round to a fleet session."""

    snapshot: FleetSnapshot
    round: int
    compute_ns: int
    credits_used: int = 0
    credits_remaining: int = 0

    @classmethod
    def from_dict(cls, d: dict) -> RoundResult:
        """Parse from API JSON."""
        return cls(
            snapshot=FleetSnapshot.from_dict(d.get("snapshot", d)),
            round=d.get("round", 0),
            compute_ns=d.get("compute_ns", 0),
            credits_used=d.get("credits_used", 0),
            credits_remaining=d.get("credits_remaining", 0),
        )
