#![warn(clippy::all)]
#![warn(clippy::nursery)]
#![warn(clippy::pedantic)]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
#![deny(unsafe_code)]
// Allow some overly strict pedantic lints
#![allow(clippy::return_self_not_must_use)]
#![allow(clippy::similar_names)]

//! # Rovo
//!
//! A lightweight macro-based framework for generating `OpenAPI` documentation
//! from doc comments in Axum handlers.
//!
//! Rovo provides a declarative way to document your API endpoints using special
//! annotations in doc comments, automatically generating `OpenAPI` specifications
//! without manual schema definitions.
//!
//! ## Quick Start
//!
//! ```no_run
//! use rovo::{Router, rovo, routing::get, schemars::JsonSchema, aide::axum::IntoApiResponse};
//! use rovo::aide::openapi::OpenApi;
//! use rovo::{extract::Path, response::Json};
//! use serde::Serialize;
//!
//! #[derive(Serialize, JsonSchema)]
//! struct User { id: u32, name: String }
//!
//! /// Get user by ID
//! ///
//! /// # Path Parameters
//! ///
//! /// id: The user ID
//! ///
//! /// # Responses
//! ///
//! /// 200: Json<User> - User found successfully
//! /// 404: () - User not found
//! ///
//! /// # Metadata
//! ///
//! /// @tag users
//! #[rovo]
//! async fn get_user(Path(id): Path<u32>) -> impl IntoApiResponse {
//!     Json(User { id, name: "Alice".to_string() })
//! }
//!
//! # #[tokio::main]
//! # async fn main() {
//! let mut api = OpenApi::default();
//! api.info.title = "My API".to_string();
//!
//! let app = Router::new()
//!     .route("/users/{id}", get(get_user))
//!     .with_oas(api);
//! # }
//! ```
//!
//! ## Documentation Format
//!
//! Rovo uses Rust-style markdown sections in doc comments:
//!
//! ### Responses Section
//! Document HTTP response codes and their types:
//! ```text
//! /// # Responses
//! ///
//! /// 200: Json<User> - User found successfully
//! /// 404: () - User not found
//! ```
//!
//! ### Examples Section
//! Provide response examples with valid Rust expressions:
//! ```text
//! /// # Examples
//! ///
//! /// 200: User { id: 1, name: "Alice".into() }
//! /// 404: ()
//! ```
//!
//! ### Metadata Section
//! Add API metadata with annotations:
//! ```text
//! /// # Metadata
//! ///
//! /// @tag users
//! /// @security bearer_auth
//! /// @id getUserById
//! /// @hidden
//! ```
//!
//! **Available metadata annotations:**
//! - `@tag <name>` - Group endpoints by tags
//! - `@security <scheme>` - Specify security requirements
//! - `@id <operation_id>` - Set custom operation ID
//! - `@hidden` - Hide endpoint from documentation
//!
//! **Special directives:**
//! - `@rovo-ignore` - Stop processing annotations after this point

pub use rovo_macros::rovo;

// Re-export aide for convenience
pub use aide;

/// Raw schemars crate re-export, used internally by the `JsonSchema` derive.
#[doc(hidden)]
pub use ::schemars as __schemars;

/// Re-export of the [`schemars`](::schemars) crate with rovo's `JsonSchema` derive.
///
/// The `JsonSchema` derive exported here automatically resolves rovo's crate path,
/// so `#[derive(JsonSchema)]` works without a direct `schemars` dependency or any
/// helper attributes.
///
/// ```rust,ignore
/// use rovo::schemars::JsonSchema;
///
/// #[derive(JsonSchema)]
/// struct MyStruct {
///     name: String,
/// }
/// ```
pub mod schemars {
    pub use ::schemars::*;

    /// Derive macro for `JsonSchema` that automatically resolves rovo's crate path.
    pub use rovo_macros::JsonSchema;
}

// Re-export axum modules for convenience, so users don't need axum as a direct dependency
/// Re-export of axum's extract module for request data extraction.
///
/// This provides access to all axum extractors like `State`, `Path`, `Query`, `Json`, etc.
///
/// # Example
/// ```no_run
/// use rovo::extract::{State, Path};
/// ```
pub use ::axum::extract;

/// Re-export of axum's response module for building HTTP responses.
///
/// This provides access to response types like `Json`, `Html`, `Redirect`, and the
/// `IntoResponse` trait.
///
/// # Example
/// ```no_run
/// use rovo::response::{Json, IntoResponse};
/// ```
pub use ::axum::response;

/// Re-export of axum's http module for HTTP primitives.
///
/// This provides access to types like `StatusCode`, `Method`, `Uri`, `HeaderMap`, etc.
///
/// # Example
/// ```no_run
/// use rovo::http::StatusCode;
/// ```
pub use ::axum::http;

use ::axum::Extension;
use aide::axum::ApiRouter as AideApiRouter;
use aide::openapi::OpenApi;
use std::sync::Arc;

/// Trait for types that can be nested into a [`Router`].
///
/// Implemented for [`Router<S>`] (same state type, preserves `OpenAPI` docs)
/// and [`axum::Router`] (state already applied via `with_state`, e.g. for nesting
/// routers with different state types).
pub trait IntoNestRouter<S>
where
    S: Clone + Send + Sync + 'static,
{
    /// Nest this router into the parent at the given path.
    fn nest_into(self, parent: Router<S>, path: &str) -> Router<S>;
}

/// Trait for types that can be registered as routes on a [`Router`].
///
/// Implemented for [`ApiMethodRouter`] (documented routes via aide's `api_route`)
/// and [`axum::routing::MethodRouter`] (undocumented routes via plain `route`).
pub trait IntoRouteHandler<S> {
    /// Register this handler on the router at the given path.
    fn register(self, router: AideApiRouter<S>, path: &str) -> AideApiRouter<S>;
}

impl<S> IntoRouteHandler<S> for ApiMethodRouter<S>
where
    S: Clone + Send + Sync + 'static,
{
    fn register(self, router: AideApiRouter<S>, path: &str) -> AideApiRouter<S> {
        router.api_route(path, self.inner)
    }
}

impl<S> IntoRouteHandler<S> for ::axum::routing::MethodRouter<S>
where
    S: Clone + Send + Sync + 'static,
{
    fn register(self, router: AideApiRouter<S>, path: &str) -> AideApiRouter<S> {
        router.route(path, self)
    }
}

impl<S> IntoNestRouter<S> for Router<S>
where
    S: Clone + Send + Sync + 'static,
{
    fn nest_into(self, mut parent: Self, path: &str) -> Self {
        parent.inner = parent.inner.nest(path, self.inner);
        if parent.oas_spec.is_none() && self.oas_spec.is_some() {
            parent.oas_spec = self.oas_spec;
            parent.oas_route = self.oas_route;
        }
        parent
    }
}

impl<S> IntoNestRouter<S> for StatefulRouter
where
    S: Clone + Send + Sync + 'static,
{
    fn nest_into(self, mut parent: Router<S>, path: &str) -> Router<S> {
        parent.inner = parent.inner.nest_api_service(path, self.inner);
        if parent.oas_spec.is_none() && self.oas_spec.is_some() {
            parent.oas_spec = self.oas_spec;
            parent.oas_route = self.oas_route;
        }
        parent
    }
}

impl<S> IntoNestRouter<S> for ::axum::Router
where
    S: Clone + Send + Sync + 'static,
{
    fn nest_into(self, mut parent: Router<S>, path: &str) -> Router<S> {
        parent.inner = parent.inner.nest_api_service(path, self);
        parent
    }
}

/// A drop-in replacement for `axum::Router` that adds `OpenAPI` documentation support.
///
/// This Router works seamlessly with handlers decorated with `#[rovo]` and provides
/// a fluent API for building documented APIs with Swagger UI integration.
///
/// # Example
/// ```no_run
/// use rovo::{Router, rovo, routing::get, aide::axum::IntoApiResponse};
/// use rovo::aide::openapi::OpenApi;
/// use rovo::response::Json;
///
/// #[rovo]
/// async fn documented_handler() -> impl IntoApiResponse {
///     Json(())
/// }
///
/// # #[tokio::main]
/// # async fn main() {
/// let mut api = OpenApi::default();
/// api.info.title = "My API".to_string();
///
/// let app = Router::new()
///     .route("/documented", get(documented_handler))
///     .with_oas(api);
/// # }
/// ```
pub struct Router<S = ()> {
    inner: AideApiRouter<S>,
    oas_spec: Option<OpenApi>,
    oas_route: String,
}

impl<S> Router<S>
where
    S: Clone + Send + Sync + 'static,
{
    /// Create a new Router
    #[must_use]
    pub fn new() -> Self {
        Self {
            inner: AideApiRouter::new(),
            oas_spec: None,
            oas_route: "/api.json".to_string(),
        }
    }

    /// Add a route to the router
    ///
    /// Accepts both documented routes (from [`routing::get`], [`routing::post`], etc.)
    /// and undocumented routes (from [`routing::any`] or plain `axum::routing::MethodRouter`).
    /// Undocumented routes bypass `OpenAPI` schema generation.
    pub fn route<M>(mut self, path: &str, method_router: M) -> Self
    where
        M: IntoRouteHandler<S>,
    {
        self.inner = method_router.register(self.inner, path);
        self
    }

    /// Nest another router at the given path
    ///
    /// Accepts both a [`Router<S>`] (same state type) and an [`axum::Router`]
    /// (state already applied via [`with_state`](Self::with_state)).
    /// The latter enables nesting routers with different state types:
    ///
    /// ```no_run
    /// use rovo::{Router, rovo, routing::get, aide::axum::IntoApiResponse};
    /// use rovo::extract::State;
    /// use rovo::response::Json;
    ///
    /// #[derive(Clone)]
    /// struct StateA;
    /// #[derive(Clone)]
    /// struct StateB;
    ///
    /// # #[rovo]
    /// # async fn handler_a(State(_): State<StateA>) -> impl IntoApiResponse { Json(()) }
    /// # #[rovo]
    /// # async fn handler_b(State(_): State<StateB>) -> impl IntoApiResponse { Json(()) }
    ///
    /// let app = Router::<()>::new()
    ///     .nest("/a", Router::new().route("/x", get(handler_a)).with_state(StateA))
    ///     .nest("/b", Router::new().route("/y", get(handler_b)).with_state(StateB))
    ///     .finish();
    /// ```
    #[must_use]
    pub fn nest<N: IntoNestRouter<S>>(self, path: &str, router: N) -> Self {
        router.nest_into(self, path)
    }

    /// Configure `OpenAPI` spec with default routes (/api.json and /api.yaml)
    ///
    /// This automatically sets up endpoints for both JSON and YAML formats.
    ///
    /// # Memory Efficiency
    ///
    /// The `OpenAPI` spec is serialized to JSON and YAML at startup, then the
    /// original struct is dropped to minimize memory usage. Only the pre-serialized
    /// strings are kept in memory.
    ///
    /// If you need runtime access to the `OpenApi` struct (e.g., in handlers via
    /// `Extension<Arc<OpenApi>>`), use [`finish_api_with_extension`](Self::finish_api_with_extension) instead.
    #[must_use]
    pub fn with_oas(mut self, api: OpenApi) -> Self {
        self.oas_spec = Some(api);
        self.oas_route = "/api.json".to_string();
        self
    }

    /// Configure `OpenAPI` spec with custom base route
    ///
    /// This sets up endpoints with the specified base route.
    /// For example, "/openapi" creates:
    /// - /openapi.json
    /// - /openapi.yaml
    ///
    /// # Memory Efficiency
    ///
    /// The `OpenAPI` spec is serialized to JSON and YAML at startup, then the
    /// original struct is dropped to minimize memory usage. Only the pre-serialized
    /// strings are kept in memory.
    ///
    /// If you need runtime access to the `OpenApi` struct (e.g., in handlers via
    /// `Extension<Arc<OpenApi>>`), use [`finish_api_with_extension`](Self::finish_api_with_extension) instead.
    pub fn with_oas_route(mut self, api: OpenApi, route: impl Into<String>) -> Self {
        self.oas_spec = Some(api);
        let route_str = route.into();
        // Remove extension if provided
        let base_route = route_str
            .strip_suffix(".json")
            .or_else(|| route_str.strip_suffix(".yaml"))
            .or_else(|| route_str.strip_suffix(".yml"))
            .unwrap_or(&route_str);
        self.oas_route = format!("{base_route}.json");
        self
    }

    /// Add Swagger UI route at the specified path
    #[cfg(feature = "swagger")]
    #[must_use]
    pub fn with_swagger(mut self, swagger_path: &str) -> Self
    where
        S: Clone + Send + Sync + 'static,
    {
        let api_route = self.oas_route.clone();
        self.inner = self.inner.route(
            swagger_path,
            aide::swagger::Swagger::new(&api_route).axum_route(),
        );
        self
    }

    /// Add Redoc UI route at the specified path
    #[cfg(feature = "redoc")]
    #[must_use]
    pub fn with_redoc(mut self, redoc_path: &str) -> Self
    where
        S: Clone + Send + Sync + 'static,
    {
        let api_route = self.oas_route.clone();
        self.inner = self
            .inner
            .route(redoc_path, aide::redoc::Redoc::new(&api_route).axum_route());
        self
    }

    /// Add Scalar UI route at the specified path
    #[cfg(feature = "scalar")]
    #[must_use]
    pub fn with_scalar(mut self, scalar_path: &str) -> Self
    where
        S: Clone + Send + Sync + 'static,
    {
        let api_route = self.oas_route.clone();
        self.inner = self.inner.route(
            scalar_path,
            aide::scalar::Scalar::new(&api_route).axum_route(),
        );
        self
    }

    /// Internal helper to wire up `OpenAPI` endpoints and extension
    fn wire_openapi_routes(self) -> (Option<::axum::Router<S>>, Option<AideApiRouter<S>>)
    where
        S: Clone + Send + Sync + 'static,
    {
        if let Some(api) = self.oas_spec {
            let oas_route = self.oas_route.clone();

            // Finish API first to populate it with routes
            let mut api_mut = api;
            let axum_router = self.inner.finish_api(&mut api_mut);

            // Pre-serialize once at startup to avoid cloning on each request
            let json_bytes: ::axum::body::Bytes = serde_json::to_vec(&api_mut)
                .expect("Failed to serialize OpenAPI spec to JSON")
                .into();
            let yaml_bytes: ::axum::body::Bytes = serde_yaml::to_string(&api_mut)
                .expect("Failed to serialize OpenAPI spec to YAML")
                .into();

            // Determine base route (without extension)
            let base_route = oas_route.strip_suffix(".json").unwrap_or(&oas_route);

            // Add JSON endpoint - returns pre-serialized bytes
            let json_for_handler = json_bytes;
            let router_with_json = axum_router.route(
                &oas_route,
                ::axum::routing::get(move || {
                    let json = json_for_handler.clone();
                    async move {
                        (
                            [(::axum::http::header::CONTENT_TYPE, "application/json")],
                            json,
                        )
                    }
                }),
            );

            // Add YAML endpoint - returns pre-serialized bytes
            let yaml_route = format!("{base_route}.yaml");
            let yaml_for_handler = yaml_bytes.clone();
            let router_with_yaml = router_with_json.route(
                &yaml_route,
                ::axum::routing::get(move || {
                    let yaml = yaml_for_handler.clone();
                    async move {
                        (
                            [(::axum::http::header::CONTENT_TYPE, "application/x-yaml")],
                            yaml,
                        )
                    }
                }),
            );

            // Add YML endpoint (alias for YAML) - reuses pre-serialized bytes
            let yml_route = format!("{base_route}.yml");
            let yml_for_handler = yaml_bytes;
            let router_with_yml = router_with_yaml.route(
                &yml_route,
                ::axum::routing::get(move || {
                    let yaml = yml_for_handler.clone();
                    async move {
                        (
                            [(::axum::http::header::CONTENT_TYPE, "application/x-yaml")],
                            yaml,
                        )
                    }
                }),
            );

            // No Extension layer - the OpenApi struct is dropped after serialization
            // to minimize memory usage. Use finish_api_with_extension() if you need
            // runtime access to the spec.
            (Some(router_with_yml), None)
        } else {
            // No OAS spec, return the inner router
            (None, Some(self.inner))
        }
    }

    /// Provide state to the router, producing a [`StatefulRouter`].
    ///
    /// The returned [`StatefulRouter`] preserves `OpenAPI` documentation and can be:
    /// - **Nested** into a parent [`Router`] with a different state type
    /// - **Finalized** with [`.finish()`](StatefulRouter::finish) to get an `axum::Router`
    ///   for serving
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use rovo::{Router, rovo, routing::get, aide::axum::IntoApiResponse};
    /// # use rovo::aide::openapi::OpenApi;
    /// # use rovo::response::Json;
    /// # #[rovo]
    /// # async fn handler() -> impl IntoApiResponse { Json(()) }
    /// let mut api = OpenApi::default();
    /// api.info.title = "My API".to_string();
    ///
    /// let app = Router::new()
    ///     .route("/path", get(handler))
    ///     .with_oas(api)
    ///     .with_state(())
    ///     .finish();
    /// ```
    #[must_use]
    pub fn with_state(self, state: S) -> StatefulRouter {
        StatefulRouter {
            inner: self.inner.with_state(state),
            oas_spec: self.oas_spec,
            oas_route: self.oas_route,
        }
    }

    /// Finalize the API without state
    pub fn finish(self) -> ::axum::Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        let (with_oas, without_oas) = self.wire_openapi_routes();

        with_oas.unwrap_or_else(|| {
            without_oas.map_or_else(
                || unreachable!("Either with_oas or without_oas must be Some"),
                std::convert::Into::into,
            )
        })
    }

    /// Finish building the API and return an axum Router for further configuration
    pub fn finish_api(self, api: &mut aide::openapi::OpenApi) -> ::axum::Router<S> {
        self.inner.finish_api(api)
    }

    /// Finish the API with `OpenAPI` spec embedded via Extension layer
    ///
    /// The spec is wrapped in `Arc<OpenApi>` for efficient sharing. Use this method
    /// when you need runtime access to the `OpenAPI` spec in your handlers.
    ///
    /// # Example
    ///
    /// ```ignore
    /// use std::sync::Arc;
    /// use axum::Extension;
    /// use aide::openapi::OpenApi;
    ///
    /// async fn handler(Extension(api): Extension<Arc<OpenApi>>) {
    ///     // Access the OpenAPI spec at runtime
    ///     println!("API title: {}", api.info.title);
    /// }
    /// ```
    ///
    /// # Note
    ///
    /// This keeps the full `OpenApi` struct in memory. For large APIs where you don't
    /// need runtime access, prefer using [`with_oas`](Self::with_oas) which only keeps
    /// pre-serialized strings in memory.
    pub fn finish_api_with_extension(self, api: aide::openapi::OpenApi) -> ::axum::Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        let mut api_mut = api;
        let router = self.inner.finish_api(&mut api_mut);
        router.layer(Extension(Arc::new(api_mut)))
    }

    /// Convert into the underlying aide `ApiRouter`
    pub fn into_inner(self) -> AideApiRouter<S> {
        self.inner
    }
}

/// A router whose state has been provided via [`Router::with_state`].
///
/// This type preserves `OpenAPI` documentation and can be:
/// - **Nested** into a parent [`Router`] with any state type
/// - **Finalized** with [`.finish()`](Self::finish) to produce an `axum::Router` for serving
///
/// # Example
///
/// ```no_run
/// use rovo::{Router, rovo, routing::get, aide::axum::IntoApiResponse};
/// use rovo::extract::State;
/// use rovo::response::Json;
///
/// #[derive(Clone)]
/// struct StateA;
/// #[derive(Clone)]
/// struct StateB;
///
/// # #[rovo]
/// # async fn handler_a(State(_): State<StateA>) -> impl IntoApiResponse { Json(()) }
/// # #[rovo]
/// # async fn handler_b(State(_): State<StateB>) -> impl IntoApiResponse { Json(()) }
///
/// let app = Router::<()>::new()
///     .nest("/a", Router::new().route("/x", get(handler_a)).with_state(StateA))
///     .nest("/b", Router::new().route("/y", get(handler_b)).with_state(StateB))
///     .finish();
/// ```
pub struct StatefulRouter {
    inner: AideApiRouter<()>,
    oas_spec: Option<OpenApi>,
    oas_route: String,
}

impl StatefulRouter {
    /// Finalize into an `axum::Router` for serving.
    ///
    /// This wires up `OpenAPI` spec endpoints (JSON/YAML) if configured,
    /// then returns a ready-to-serve `axum::Router`.
    pub fn finish(self) -> ::axum::Router {
        let router: Router<()> = Router {
            inner: self.inner,
            oas_spec: self.oas_spec,
            oas_route: self.oas_route,
        };
        router.finish()
    }
}

impl<S> Default for Router<S>
where
    S: Clone + Send + Sync + 'static,
{
    fn default() -> Self {
        Self::new()
    }
}

/// Trait for handlers that carry their own documentation.
///
/// This trait is automatically implemented by the `#[rovo]` macro for decorated handlers.
/// It provides methods to convert the handler into documented route handlers for each HTTP method.
///
/// You typically won't implement this trait manually - instead, use the `#[rovo]` macro
/// on your handler functions.
pub trait IntoApiMethodRouter<S = ()> {
    /// Convert into a GET route with documentation
    fn into_get_route(self) -> aide::axum::routing::ApiMethodRouter<S>;
    /// Convert into a POST route with documentation
    fn into_post_route(self) -> aide::axum::routing::ApiMethodRouter<S>;
    /// Convert into a PATCH route with documentation
    fn into_patch_route(self) -> aide::axum::routing::ApiMethodRouter<S>;
    /// Convert into a DELETE route with documentation
    fn into_delete_route(self) -> aide::axum::routing::ApiMethodRouter<S>;
    /// Convert into a PUT route with documentation
    fn into_put_route(self) -> aide::axum::routing::ApiMethodRouter<S>;
}

/// Wrapper around `ApiMethodRouter` that provides method chaining for documented handlers.
///
/// This type is returned by routing functions like `get()`, `post()`, etc. and allows
/// chaining methods with the exact same names as axum (`.post()`, `.patch()`, etc.) while
/// accepting documented handlers decorated with `#[rovo]`.
///
/// # Example
/// ```no_run
/// use rovo::{Router, rovo, routing::{get, post, patch, delete}, aide::axum::IntoApiResponse};
/// use rovo::response::Json;
///
/// #[rovo]
/// async fn list_items() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn create_item() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn get_item() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn update_item() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn delete_item() -> impl IntoApiResponse { Json(()) }
///
/// # #[tokio::main]
/// # async fn main() {
/// Router::new()
///     .route("/items", get(list_items).post(create_item))
///     .route("/items/{id}", get(get_item).patch(update_item).delete(delete_item));
/// # }
/// ```
pub struct ApiMethodRouter<S = ()> {
    inner: aide::axum::routing::ApiMethodRouter<S>,
}

impl<S> ApiMethodRouter<S>
where
    S: Clone + Send + Sync + 'static,
{
    /// Create a new `ApiMethodRouter` from aide's `ApiMethodRouter`
    #[must_use]
    pub const fn new(inner: aide::axum::routing::ApiMethodRouter<S>) -> Self {
        Self { inner }
    }

    /// Chain a POST handler
    pub fn post<H>(self, handler: H) -> Self
    where
        H: IntoApiMethodRouter<S>,
    {
        Self {
            inner: self.inner.merge(handler.into_post_route()),
        }
    }

    /// Chain a GET handler
    pub fn get<H>(self, handler: H) -> Self
    where
        H: IntoApiMethodRouter<S>,
    {
        Self {
            inner: self.inner.merge(handler.into_get_route()),
        }
    }

    /// Chain a PATCH handler
    pub fn patch<H>(self, handler: H) -> Self
    where
        H: IntoApiMethodRouter<S>,
    {
        Self {
            inner: self.inner.merge(handler.into_patch_route()),
        }
    }

    /// Chain a DELETE handler
    pub fn delete<H>(self, handler: H) -> Self
    where
        H: IntoApiMethodRouter<S>,
    {
        Self {
            inner: self.inner.merge(handler.into_delete_route()),
        }
    }

    /// Chain a PUT handler
    pub fn put<H>(self, handler: H) -> Self
    where
        H: IntoApiMethodRouter<S>,
    {
        Self {
            inner: self.inner.merge(handler.into_put_route()),
        }
    }
}

impl<S> From<ApiMethodRouter<S>> for aide::axum::routing::ApiMethodRouter<S> {
    fn from(router: ApiMethodRouter<S>) -> Self {
        router.inner
    }
}

/// Drop-in replacement routing functions that work with `#[rovo]` decorated handlers.
///
/// These functions provide the same API as axum's routing functions but accept
/// handlers decorated with `#[rovo]` and automatically include their documentation.
///
/// # Example
/// ```no_run
/// use rovo::{Router, rovo, routing::{get, post, patch, delete}, aide::axum::IntoApiResponse};
/// use rovo::response::Json;
///
/// #[rovo]
/// async fn list_items() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn create_item() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn get_item() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn update_item() -> impl IntoApiResponse { Json(()) }
/// #[rovo]
/// async fn delete_item() -> impl IntoApiResponse { Json(()) }
///
/// # #[tokio::main]
/// # async fn main() {
/// Router::new()
///     .route("/items", get(list_items).post(create_item))
///     .route("/items/{id}", get(get_item).patch(update_item).delete(delete_item));
/// # }
/// ```
pub mod routing {
    use super::{ApiMethodRouter, IntoApiMethodRouter};

    /// Create a route handler that matches any HTTP method.
    ///
    /// Unlike other routing functions in this module, `any` accepts a plain axum handler
    /// rather than a `#[rovo]` decorated handler. The route bypasses `OpenAPI` documentation,
    /// making it suitable for proxy/fallback routes and catch-all patterns like `{*path}`.
    ///
    /// # Example
    /// ```no_run
    /// use rovo::{Router, routing};
    ///
    /// async fn proxy(axum::extract::Path(path): axum::extract::Path<String>) -> String {
    ///     format!("proxying: {path}")
    /// }
    ///
    /// let app = Router::<()>::new()
    ///     .route("/api/{*path}", routing::any(proxy))
    ///     .finish();
    /// ```
    pub fn any<H, T, S>(handler: H) -> ::axum::routing::MethodRouter<S>
    where
        H: ::axum::handler::Handler<T, S>,
        S: Clone + Send + Sync + 'static,
        T: 'static,
    {
        ::axum::routing::any(handler)
    }

    /// Create a GET route with documentation from a `#[rovo]` decorated handler.
    pub fn get<S, H>(handler: H) -> ApiMethodRouter<S>
    where
        H: IntoApiMethodRouter<S>,
        S: Clone + Send + Sync + 'static,
    {
        ApiMethodRouter::new(handler.into_get_route())
    }

    /// Create a POST route with documentation from a `#[rovo]` decorated handler.
    pub fn post<S, H>(handler: H) -> ApiMethodRouter<S>
    where
        H: IntoApiMethodRouter<S>,
        S: Clone + Send + Sync + 'static,
    {
        ApiMethodRouter::new(handler.into_post_route())
    }

    /// Create a PATCH route with documentation from a `#[rovo]` decorated handler.
    pub fn patch<S, H>(handler: H) -> ApiMethodRouter<S>
    where
        H: IntoApiMethodRouter<S>,
        S: Clone + Send + Sync + 'static,
    {
        ApiMethodRouter::new(handler.into_patch_route())
    }

    /// Create a DELETE route with documentation from a `#[rovo]` decorated handler.
    pub fn delete<S, H>(handler: H) -> ApiMethodRouter<S>
    where
        H: IntoApiMethodRouter<S>,
        S: Clone + Send + Sync + 'static,
    {
        ApiMethodRouter::new(handler.into_delete_route())
    }

    /// Create a PUT route with documentation from a `#[rovo]` decorated handler.
    pub fn put<S, H>(handler: H) -> ApiMethodRouter<S>
    where
        H: IntoApiMethodRouter<S>,
        S: Clone + Send + Sync + 'static,
    {
        ApiMethodRouter::new(handler.into_put_route())
    }
}

/// Re-exports from aide's axum integration for convenience.
pub mod axum {
    pub use aide::axum::{ApiRouter, IntoApiResponse};
}
