#![deny(missing_docs)]

//! Procedural macros for Nidus modules, providers, controllers, and routes.

mod controller;
mod diagnostics;
mod entrypoint;
mod guard;
mod injectable;
mod module;
mod pipe;
mod routes;
mod routes_metadata;
mod routes_openapi;
mod utils;

use proc_macro::TokenStream;

/// Runs an async Nidus entrypoint on a Tokio runtime.
///
/// Use `#[nidus::main]` on the crate's top-level `async fn main()` when an
/// application should use the runtime configuration provided by the Nidus
/// facade. The macro is executable behavior: it rewrites the async function
/// into a synchronous `main` that builds a Tokio runtime and blocks on the
/// original body.
///
/// Basic syntax:
///
/// ```ignore
/// #[nidus::main]
/// async fn main() -> nidus::Result<()> {
///     nidus::prelude::Nidus::bootstrap::<AppModule>()?
///         .with_router(UsersController.into_router())
///         .listen("127.0.0.1:3000")
///         .await
/// }
/// ```
///
/// The annotated function must be async, must be named `main`, and must not
/// already be wrapped in another runtime macro such as `#[tokio::main]`.
/// Returning `Result` is idiomatic because bootstrap, router construction, and
/// server startup can all fail. Common errors are using the macro on a non-async
/// function or applying it to helper functions instead of the binary entrypoint.
#[proc_macro_attribute]
pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
    entrypoint::expand(attr.into(), item.into()).into()
}

/// Declares a Nidus module from a Rust struct.
///
/// `#[module]` is valid on named structs that represent application modules.
/// It is metadata-generating behavior: the macro keeps the struct in place and
/// implements Nidus module metadata so the runtime and CLI can inspect imports,
/// providers, controllers, and exports without runtime reflection.
///
/// Basic syntax:
///
/// ```ignore
/// #[module(
///     imports = [UsersModule],
///     providers = [EmailService],
///     controllers = [HealthController],
///     exports = [EmailService],
/// )]
/// struct AppModule;
/// ```
///
/// The generated module definition composes with `Nidus::bootstrap::<AppModule>()`,
/// graph validation, CLI graph/check commands, and generated starter projects.
/// Module entries are Rust types, not string names, so missing imports or renamed
/// providers are caught by the compiler. Common errors include omitting brackets
/// around metadata lists, placing the macro on enums or tuple structs, and
/// exporting a provider that is not registered by the module or its imports.
#[proc_macro_attribute]
pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream {
    module::expand(attr.into(), item.into()).into()
}

/// Declares a struct as an injectable provider.
///
/// `#[injectable]` is valid on named structs. It is metadata-generating
/// behavior: the macro preserves the struct and implements provider registration
/// metadata so modules can list the type in `providers = [...]` and consumers can
/// request it through typed injection primitives.
///
/// Basic syntax:
///
/// ```ignore
/// #[injectable]
/// struct UsersService {
///     repository: nidus::prelude::Inject<UsersRepository>,
/// }
/// ```
///
/// Injectable providers compose with `Inject<T>`, `Optional<T>`, `Lazy<T>`,
/// `Factory<T>`, and request-scoped providers exposed by the Nidus core. The
/// macro is intentionally explicit: constructor behavior is not hidden behind
/// runtime string lookup. Common errors are applying it to tuple structs,
/// unsupported generic/lifetime shapes, or fields that are not valid provider
/// dependencies.
#[proc_macro_attribute]
pub fn injectable(attr: TokenStream, item: TokenStream) -> TokenStream {
    injectable::expand(attr.into(), item.into()).into()
}

/// Declares the path prefix for a controller type.
///
/// `#[controller]` is valid on structs that own HTTP handler methods. It is
/// metadata-generating behavior by itself: it records the controller prefix and
/// exposes controller metadata used by `#[routes]`, CLI route inspection, and
/// OpenAPI tooling.
///
/// Basic syntax:
///
/// ```ignore
/// #[controller("/users")]
/// struct UsersController {
///     service: UsersService,
/// }
/// ```
///
/// `#[controller("/users")]` combines with route methods inside a `#[routes]`
/// impl block. The final route path is the controller prefix plus each method's
/// route path, for example `"/users"` and `"/:id"` become `"/users/:id"`.
/// Common errors are missing the string literal path, using an empty dynamic
/// segment such as `"/:"`, or expecting this macro alone to register Tower
/// routes. Executable routing is generated by `#[routes]`.
#[proc_macro_attribute]
pub fn controller(attr: TokenStream, item: TokenStream) -> TokenStream {
    controller::expand(attr.into(), item.into()).into()
}

/// Generates route metadata and executable Axum routers for a controller impl.
///
/// `#[routes]` is valid on inherent `impl` blocks for controller types. It is
/// both metadata-generating and executable behavior: the macro validates route
/// attributes, emits `routes()` metadata for CLI/OpenAPI tooling, and generates
/// `try_into_router()` plus `into_router()` when route methods are present.
///
/// Basic syntax:
///
/// ```ignore
/// #[controller("/users")]
/// struct UsersController;
///
/// #[routes]
/// impl UsersController {
///     #[get("/:id")]
///     async fn show(
///         &self,
///         nidus::prelude::Path(id): nidus::prelude::Path<String>,
///         scoped: nidus::prelude::RequestScoped<nidus::prelude::RequestContext>,
///     ) -> nidus::prelude::Json<UserDto> {
///         nidus::prelude::Json(UserDto::from_id(id, scoped.request_id().to_owned()))
///     }
/// }
/// ```
///
/// `try_into_router()` returns `Result<Router, RoutePathError>` and should be
/// preferred when path validation errors must be handled. `into_router()` unwraps
/// that result and panics with the route error, which is convenient in examples
/// and tests where invalid static paths are programming mistakes.
///
/// Handler parameters are normal Axum extractors such as `Path<T>`, `Query<T>`,
/// `Json<T>`, and Nidus extractors such as `RequestScoped<T>`. Guards, pipes,
/// validation, and OpenAPI attributes attached to methods are stored in route
/// metadata; Tower layers and extractors are still the execution path for actual
/// request enforcement and transformation. Common errors include using route
/// macros outside a `#[routes]` impl, omitting `&self`, duplicating method
/// metadata, or assuming guard/pipe metadata automatically installs middleware.
#[proc_macro_attribute]
pub fn routes(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes::expand_routes(attr.into(), item.into()).into()
}

/// Declares a GET handler inside a `#[routes]` impl block.
///
/// `#[get]` is valid only on async controller methods that receive `&self`. It
/// is metadata for `#[routes]`; the executable Axum route is generated when the
/// enclosing impl block is expanded.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[get("/:id")]
///     async fn show(&self, Path(id): Path<String>) -> Json<UserDto> {
///         Json(self.service.find(id).await)
///     }
/// }
/// ```
///
/// The path is relative to the controller prefix. Method parameters can use Axum
/// extractors such as `Path`, `Query`, and `Json`, plus Nidus extractors such as
/// `RequestScoped<T>`. Route metadata feeds `routes()`, CLI route output, and
/// OpenAPI generation when combined with `#[openapi]`. Common errors are
/// omitting the path string, using invalid path parameters, or placing the
/// method outside `#[routes]`.
#[proc_macro_attribute]
pub fn get(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes::expand_route("get", attr.into(), item.into()).into()
}

/// Declares a POST handler inside a `#[routes]` impl block.
///
/// `#[post]` has the same placement and generation rules as `#[get]`, but emits
/// POST route metadata and executable Axum routing from the enclosing
/// `#[routes]` impl block.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[post("/")]
///     async fn create(&self, Json(input): Json<CreateUser>) -> (StatusCode, Json<UserDto>) {
///         (StatusCode::CREATED, Json(self.service.create(input).await))
///     }
/// }
/// ```
///
/// Use `Json<T>`, `Query<T>`, `Path<T>`, request extensions, or
/// `RequestScoped<T>` exactly as you would in Axum handlers. Common errors are
/// treating guard or pipe metadata as request execution by itself, duplicating
/// route method attributes, or returning a type that does not implement
/// `IntoResponse`.
#[proc_macro_attribute]
pub fn post(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes::expand_route("post", attr.into(), item.into()).into()
}

/// Declares a PUT handler inside a `#[routes]` impl block.
///
/// `#[put]` records PUT route metadata and participates in `#[routes]`
/// generation of `routes()`, `try_into_router()`, and `into_router()`.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[put("/:id")]
///     async fn replace(&self, Path(id): Path<String>, Json(input): Json<UserInput>) -> Json<UserDto> {
///         Json(self.service.replace(id, input).await)
///     }
/// }
/// ```
///
/// The macro is valid only on methods with a receiver. It does not run request
/// validation by itself; attach extractor types, Tower layers, validation pipes,
/// or explicit application logic for executable behavior.
#[proc_macro_attribute]
pub fn put(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes::expand_route("put", attr.into(), item.into()).into()
}

/// Declares a PATCH handler inside a `#[routes]` impl block.
///
/// `#[patch]` records PATCH route metadata and is consumed by `#[routes]` to
/// build the controller router.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[patch("/:id")]
///     async fn update(&self, Path(id): Path<String>, Json(input): Json<UpdateUser>) -> Json<UserDto> {
///         Json(self.service.update(id, input).await)
///     }
/// }
/// ```
///
/// Use this macro for partial update endpoints where the handler return type is
/// any Axum-compatible `IntoResponse`. Common errors are invalid relative paths
/// and forgetting that the controller prefix is prepended by generated router
/// construction.
#[proc_macro_attribute]
pub fn patch(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes::expand_route("patch", attr.into(), item.into()).into()
}

/// Declares a DELETE handler inside a `#[routes]` impl block.
///
/// `#[delete]` records DELETE route metadata and is consumed by `#[routes]` to
/// build executable router methods.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[delete("/:id")]
///     async fn delete(&self, Path(id): Path<String>) -> StatusCode {
///         self.service.delete(id).await;
///         StatusCode::NO_CONTENT
///     }
/// }
/// ```
///
/// The generated route composes with normal Axum extractors and responses.
/// Guard, pipe, validate, and OpenAPI annotations on the same method are
/// available through route metadata for tooling and explicit integration code.
#[proc_macro_attribute]
pub fn delete(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes::expand_route("delete", attr.into(), item.into()).into()
}

/// Declares OpenAPI metadata for a controller route method.
///
/// `#[openapi]` is valid only on methods inside a `#[routes]` impl block. It is
/// metadata-only behavior: it does not change request execution, but it enriches
/// the `RouteMetadata` emitted by `#[routes]` so CLI and OpenAPI builders can
/// generate useful documentation.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[get("/:id")]
///     #[openapi(
///         summary = "Fetch a user by id",
///         tags = ["users"],
///         status = 200,
///         response = UserDto,
///     )]
///     async fn show(&self, Path(id): Path<String>) -> Json<UserDto> {
///         Json(self.service.find(id).await)
///     }
/// }
/// ```
///
/// Supported metadata includes summaries, tags, status codes, request schemas,
/// and response schemas. DTO schema names are recorded for tooling; they do not
/// serialize or validate requests at runtime. Common errors include missing
/// `summary`, non-string tags, invalid status values, duplicate metadata, or
/// placing the attribute outside a route method.
#[proc_macro_attribute]
pub fn openapi(attr: TokenStream, item: TokenStream) -> TokenStream {
    routes_openapi::expand_openapi(attr.into(), item.into()).into()
}

/// Declares guard metadata for a controller route method.
///
/// `#[guard]` is valid only on methods inside a `#[routes]` impl block. It is
/// metadata-only behavior in the macro layer: the guard type is recorded on the
/// generated `RouteMetadata`, while actual request enforcement should be wired
/// through Tower layers, Axum extractors, or explicit handler/service code.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl AdminController {
///     #[get("/reports")]
///     #[guard(AdminGuard)]
///     async fn reports(&self) -> Json<Vec<ReportDto>> {
///         Json(self.service.reports().await)
///     }
/// }
/// ```
///
/// Guard metadata composes with `nidus-auth` guard implementations and CLI or
/// OpenAPI route inspection. Common errors are omitting the guard type, using
/// the attribute on a non-route item, or assuming metadata alone installs a
/// `GuardLayer`.
#[proc_macro_attribute]
pub fn guard(attr: TokenStream, item: TokenStream) -> TokenStream {
    guard::expand(attr.into(), item.into()).into()
}

/// Declares pipe metadata for a controller route method.
///
/// `#[pipe]` is valid only on methods inside a `#[routes]` impl block. It is
/// metadata-only behavior in the macro layer: the pipe type is recorded on the
/// generated route metadata for tooling and explicit integration, but request
/// transformation still happens through extractors, validation wrappers, Tower
/// layers, or handler code.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[post("/")]
///     #[pipe(TrimInputPipe)]
///     async fn create(&self, Json(input): Json<CreateUser>) -> Json<UserDto> {
///         Json(self.service.create(input).await)
///     }
/// }
/// ```
///
/// Pipes compose with validation metadata and Nidus validation types, but the
/// macro intentionally avoids hidden runtime mutation. Common errors include
/// omitting the pipe type or placing `#[pipe]` outside a controller route method.
#[proc_macro_attribute]
pub fn pipe(attr: TokenStream, item: TokenStream) -> TokenStream {
    pipe::expand(attr.into(), item.into()).into()
}

/// Marks a controller route method as participating in validation metadata.
///
/// `#[validate]` is valid only on methods inside a `#[routes]` impl block. It is
/// metadata-only behavior: generated `RouteMetadata` records that the route is
/// intended to use validation, while runtime validation should be expressed with
/// typed extractors such as `ValidatedJson<T>`, validation pipes, Tower layers,
/// or explicit handler logic.
///
/// Basic syntax:
///
/// ```ignore
/// #[routes]
/// impl UsersController {
///     #[post("/")]
///     #[validate]
///     async fn create(&self, input: ValidatedJson<CreateUser>) -> Json<UserDto> {
///         Json(self.service.create(input.into_inner()).await)
///     }
/// }
/// ```
///
/// The attribute composes with `#[openapi]`, `#[guard]`, and `#[pipe]` by adding
/// validation intent to the same route metadata record. Common errors are using
/// it outside a route method or expecting validation to run without a validation
/// extractor, pipe, or layer in the executable request path.
#[proc_macro_attribute]
pub fn validate(attr: TokenStream, item: TokenStream) -> TokenStream {
    pipe::expand_validate(attr.into(), item.into()).into()
}
