"""Main I2P Router — lifecycle management and state machine. Ported from net.i2p.router.Router. """ from __future__ import annotations import enum import time from dataclasses import asdict from i2p_router.config import RouterConfig from i2p_router.core import RouterContext class RouterState(enum.Enum): """Router lifecycle states.""" STOPPED = "stopped" STARTING = "starting" RUNNING = "running" SHUTTING_DOWN = "shutting_down" class Router: """Main I2P router instance. Manages the router lifecycle (start, run, shutdown) and owns the RouterContext which wires together all subsystems. """ def __init__(self, config: RouterConfig | None = None) -> None: self._config = config or RouterConfig() self._state = RouterState.STOPPED self._context: RouterContext | None = None self._started_at: float | None = None @property def state(self) -> RouterState: """Current router state.""" return self._state @property def config(self) -> RouterConfig: """Router configuration.""" return self._config @property def uptime_seconds(self) -> float: """Seconds since the router was started, or 0.0 if stopped.""" if self._started_at is None: return 0.0 return time.monotonic() - self._started_at def start(self) -> None: """Start the router. 1. If already RUNNING, this is a no-op (idempotent). 2. Validate config. 3. Transition to STARTING. 4. Create RouterContext and initialize subsystems. 5. Transition to RUNNING. Raises ------ ValueError If the configuration is invalid. State remains STOPPED. """ if self._state == RouterState.RUNNING: return # Validate before changing state self._config.validate() self._state = RouterState.STARTING # Create the router context (wires all subsystems) self._context = RouterContext() self._started_at = time.monotonic() self._state = RouterState.RUNNING def shutdown(self) -> None: """Gracefully shut down the router. If already STOPPED, this is a no-op. Transitions through SHUTTING_DOWN to STOPPED. """ if self._state == RouterState.STOPPED: return self._state = RouterState.SHUTTING_DOWN # Clean up self._context = None self._started_at = None self._state = RouterState.STOPPED def get_status(self) -> dict: """Return router status including state, uptime, and config. Returns ------- dict Status dictionary with state, uptime_seconds, and config fields. """ return { "state": self._state.value, "uptime_seconds": self.uptime_seconds, "config": asdict(self._config), }