"""Type stubs for horus._horus (Rust extension module)."""

from typing import Any, Dict, List, Optional, Tuple

# ── Exceptions ────────────────────────────────────────────────────────────────

class HorusNotFoundError(Exception):
    """Raised when a topic, frame, or node is not found."""
    ...

class HorusTransformError(Exception):
    """Raised when a coordinate transform fails (extrapolation, stale data)."""
    ...

class HorusTimeoutError(Exception):
    """Raised when a blocking operation times out."""
    ...

# ── Node ──────────────────────────────────────────────────────────────────────

class NodeState:
    UNINITIALIZED: str
    INITIALIZING: str
    RUNNING: str
    STOPPING: str
    STOPPED: str
    ERROR: str
    CRASHED: str
    @property
    def name(self) -> str: ...

class NodeInfo:
    @property
    def name(self) -> str: ...
    @property
    def state(self) -> NodeState: ...
    def log_info(self, message: str) -> None: ...
    def log_warning(self, message: str) -> None: ...
    def log_error(self, message: str) -> None: ...
    def log_debug(self, message: str) -> None: ...
    def log_pub(self, topic: str, data_repr: str, ipc_ns: int) -> None: ...
    def log_sub(self, topic: str, data_repr: str, ipc_ns: int) -> None: ...
    def get_metrics(self) -> Dict[str, float]: ...
    def get_uptime(self) -> float: ...
    def avg_tick_duration_ms(self) -> float: ...
    def tick_count(self) -> int: ...
    def error_count(self) -> int: ...
    def successful_ticks(self) -> int: ...
    def failed_ticks(self) -> int: ...
    def transition_to_error(self, error_msg: str) -> None: ...
    def get_custom_data(self, key: str) -> Optional[str]: ...
    def set_custom_data(self, key: str, value: str) -> None: ...

class Node:
    def __init__(self, name: str) -> None: ...
    @property
    def name(self) -> str: ...
    def tick(self, info: Optional[NodeInfo] = None) -> None: ...
    def init(self, info: Optional[NodeInfo] = None) -> None: ...
    def shutdown(self, info: Optional[NodeInfo] = None) -> None: ...
    def set_callback(self, callback: Any) -> None: ...

# ── Topic ─────────────────────────────────────────────────────────────────────

class Topic:
    def __init__(self, endpoint: str, msg_type: str = "generic", capacity: int = 16) -> None: ...
    @property
    def name(self) -> str: ...
    def send(self, data: Any, node: Optional[NodeInfo] = None) -> None: ...
    def recv(self, py: Any = None, node: Optional[NodeInfo] = None) -> Optional[Any]: ...
    def try_recv(self) -> Optional[Any]: ...
    def read_latest(self) -> Optional[Any]: ...
    def pending_count(self) -> int: ...
    def pub_count(self) -> int: ...
    def sub_count(self) -> int: ...
    def backend_type(self) -> str: ...
    def __repr__(self) -> str: ...

# ── Scheduler ─────────────────────────────────────────────────────────────────

class Miss:
    WARN: str
    SKIP: str
    SAFE_MODE: str
    STOP: str

class SchedulerConfig:
    tick_rate: float
    fault_tolerance: bool
    memory_locking: bool
    rt_scheduling_class: bool
    profiling: bool
    black_box_enabled: bool
    black_box_size_mb: int
    budget_enforcement: bool
    deadline_monitoring: bool
    watchdog_enabled: bool
    watchdog_timeout_ms: int
    safety_monitor: bool
    max_deadline_misses: int
    numa_aware: bool
    cpu_cores: Optional[List[int]]
    metrics_interval_ms: int
    telemetry_endpoint: Optional[str]
    recording_enabled: bool
    @staticmethod
    def minimal() -> 'SchedulerConfig': ...
    @staticmethod
    def deploy() -> 'SchedulerConfig': ...
    @staticmethod
    def hard_rt() -> 'SchedulerConfig': ...
    @staticmethod
    def safety_critical() -> 'SchedulerConfig': ...

class NodeBuilder:
    def order(self, order: int) -> 'NodeBuilder': ...
    def rate_hz(self, rate: float) -> 'NodeBuilder': ...
    def deadline(self, ms: float) -> 'NodeBuilder': ...
    def budget(self, us: int) -> 'NodeBuilder': ...
    def on_miss(self, policy: str) -> 'NodeBuilder': ...
    def failure_policy(
        self, name: str, *,
        max_retries: Optional[int] = None,
        backoff_ms: Optional[int] = None,
        max_failures: Optional[int] = None,
        cooldown_ms: Optional[int] = None,
    ) -> 'NodeBuilder': ...
    def on(self, topic: str) -> 'NodeBuilder': ...
    def compute(self) -> 'NodeBuilder': ...
    def async_io(self) -> 'NodeBuilder': ...
    def done(self) -> 'Scheduler': ...

class Scheduler:
    def __init__(self, config: Optional[SchedulerConfig] = None) -> None: ...
    def node(self, node: Any) -> NodeBuilder: ...
    def add(
        self, node: Any, order: int = 100, rate_hz: Optional[float] = None,
        rt: bool = False, deadline: Optional[float] = None,
        budget: Optional[int] = None, failure_policy: Optional[str] = None,
        on_miss: Optional[str] = None,
    ) -> None: ...
    def set_node_rate(self, node_name: str, rate_hz: float) -> None: ...
    def run(self) -> None: ...
    def run_for(self, duration_seconds: float) -> None: ...
    def tick(self, node_names: List[str]) -> None: ...
    def tick_for(self, node_names: List[str], duration_seconds: float) -> None: ...
    def stop(self) -> None: ...
    def is_running(self) -> bool: ...
    def get_node_stats(self, node_name: str) -> Dict[str, Any]: ...
    def get_all_nodes(self) -> List[Dict[str, Any]]: ...
    def get_node_count(self) -> int: ...
    def has_node(self, name: str) -> bool: ...
    def get_node_names(self) -> List[str]: ...
    def get_node_info(self, name: str) -> Optional[int]: ...
    def remove_node(self, name: str) -> bool: ...
    def status(self) -> str: ...
    def capabilities(self) -> Optional[Dict[str, Any]]: ...
    def has_full_rt(self) -> bool: ...
    def degradations(self) -> List[Dict[str, Any]]: ...
    def current_tick(self) -> int: ...
    def scheduler_name(self) -> str: ...
    def safety_stats(self) -> Optional[Dict[str, Any]]: ...
    def is_recording(self) -> bool: ...
    def is_replaying(self) -> bool: ...
    def stop_recording(self) -> List[str]: ...
    @staticmethod
    def list_recordings() -> List[str]: ...
    @staticmethod
    def delete_recording(session_name: str) -> None: ...
    def set_tick_budget(self, node_name: str, us: int) -> None: ...
    def add_critical_node(self, node_name: str, timeout_ms: int) -> None: ...
    def __repr__(self) -> str: ...

# ── HFrame ────────────────────────────────────────────────────────────────────

class Transform:
    def __init__(self, translation: Optional[List[float]] = None, rotation: Optional[List[float]] = None) -> None: ...
    @staticmethod
    def identity() -> 'Transform': ...
    @property
    def translation(self) -> Tuple[float, float, float]: ...
    @property
    def rotation(self) -> Tuple[float, float, float, float]: ...
    def inverse(self) -> 'Transform': ...
    def compose(self, other: 'Transform') -> 'Transform': ...
    def __mul__(self, other: 'Transform') -> 'Transform': ...
    def __repr__(self) -> str: ...

class HFrameConfig:
    def __init__(self, max_frames: int = 256, buffer_size: int = 64) -> None: ...

class HFrame:
    def __init__(self, config: Optional[HFrameConfig] = None) -> None: ...
    def register_frame(self, name: str, parent: Optional[str] = None) -> int: ...
    def register_static_frame(self, name: str, transform: Transform, parent: Optional[str] = None) -> int: ...
    def unregister_frame(self, name: str) -> None: ...
    def frame_id(self, name: str) -> Optional[int]: ...
    def frame_name(self, id: int) -> Optional[str]: ...
    def has_frame(self, name: str) -> bool: ...
    def update(self, name: str, transform: Transform, timestamp_ns: Optional[int] = None) -> None: ...
    def tf(self, src: str, dst: str) -> Transform: ...
    def tf_at(self, src: str, dst: str, timestamp_ns: int) -> Transform: ...
    def can_transform(self, src: str, dst: str) -> bool: ...
    def frame_count(self) -> int: ...
    def all_frames(self) -> List[str]: ...
    def parent(self, name: str) -> Optional[str]: ...
    def children(self, name: str) -> List[str]: ...
    def depth(self, name: str) -> int: ...
    # Phase 2: Waits & Staleness
    def wait_for_transform(self, src: str, dst: str, timeout_sec: float = 5.0) -> Transform: ...
    def tf_at_with_tolerance(self, src: str, dst: str, timestamp_ns: int, tolerance_ns: int = 100_000_000) -> Transform: ...
    def is_stale(self, name: str, max_age_sec: float = 1.0) -> bool: ...
    def time_since_last_update(self, name: str) -> float: ...
    def set_static_transform(self, name: str, transform: Transform) -> None: ...
    def transform_vector(self, src: str, dst: str, vector: List[float]) -> Tuple[float, float, float]: ...
    def can_transform_at(self, src: str, dst: str, timestamp_ns: int) -> bool: ...
    # Phase 3: Diagnostics
    def stats(self) -> Dict[str, Any]: ...
    def validate(self) -> None: ...
    def frame_info(self, name: str) -> Optional[Dict[str, Any]]: ...
    def __repr__(self) -> str: ...

def get_timestamp_ns() -> int: ...

# ── Domain Types ──────────────────────────────────────────────────────────────

class Image:
    def __init__(self, height: int, width: int, encoding: str = "rgb8") -> None: ...
    @property
    def height(self) -> int: ...
    @property
    def width(self) -> int: ...
    @property
    def encoding(self) -> str: ...
    def to_numpy(self) -> Any: ...
    @staticmethod
    def from_numpy(array: Any, encoding: str = "rgb8") -> 'Image': ...

class PointCloud:
    def __init__(self) -> None: ...
    def to_numpy(self) -> Any: ...

class DepthImage:
    def __init__(self, height: int, width: int) -> None: ...
    def to_numpy(self) -> Any: ...
    @staticmethod
    def from_numpy(array: Any) -> 'DepthImage': ...

# ── GPU Utilities ─────────────────────────────────────────────────────────────

def cuda_is_available() -> bool: ...
def cuda_device_count() -> int: ...

# ── Priority ──────────────────────────────────────────────────────────────────

class Priority:
    CRITICAL: int  # 0
    HIGH: int      # 10
    NORMAL: int    # 50
    LOW: int       # 80
    BACKGROUND: int  # 100

def get_version() -> str: ...
