//! Mizan axum HTTP adapter — typed RPC over `mizan-core`'s function registry.
//!
//! Usage:
//!   ```ignore
//!   use axum::Router;
//!   use mizan_axum::router;
//!
//!   #[tokio::main]
//!   async fn main() {
//!       let app = Router::new().nest("/api/mizan", router());
//!       let listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await.unwrap();
//!       axum::serve(listener, app).await.unwrap();
//!   }
//!   ```
//!
//! Exposed endpoints (mirroring `mizan-fastapi` / `mizan-django`):
//!   * `GET  /session/`  — session-init probe (placeholder CSRF token)
//!   * `POST /call/`     — RPC dispatch with invalidate+merge response
//!   * `GET  /ctx/:name/` — bundled context fetch

mod errors;
mod handlers;

pub use errors::ApiError;
pub use handlers::{
    context_fetch, function_call, session_init, AppStateAny, CallBody, CallResponse,
};

use axum::routing::{get, post};
use axum::Router;
use std::any::Any;
use std::sync::Arc;

/// Build the Mizan router with user-supplied app state. The state is
/// type-erased into an `Arc<dyn Any + Send + Sync>` and threaded into every
/// dispatch via `RequestHandle`. Handlers downcast to their concrete state
/// type.
///
/// Mount under a prefix:
///   `Router::new().nest("/api/mizan", router(my_state))`.
pub fn router<S>(state: S) -> Router
where
    S: Any + Send + Sync + 'static,
{
    let state: AppStateAny = Arc::new(state);
    Router::new()
        .route("/session/", get(handlers::session_init))
        .route("/call/", post(handlers::function_call))
        .route("/ctx/:context_name/", get(handlers::context_fetch))
        .with_state(state)
}

/// Router variant for callers that have no app state to thread — the
/// dispatch path receives a unit-typed handle. Used by the AFI fixture
/// and other stateless test apps.
pub fn router_stateless() -> Router {
    router(())
}
