use nidus::prelude::*;

#[derive(utoipa::ToSchema)]
#[controller("/users")]
struct UsersController;
struct AuthGuard;
struct ValidationPipe;
#[derive(utoipa::ToSchema)]
struct CreateUserDto;
#[derive(utoipa::ToSchema)]
struct UserDto;

#[async_trait::async_trait]
impl Guard<()> for AuthGuard {
    async fn check(&self, _ctx: GuardContext<()>) -> std::result::Result<(), GuardError> {
        Ok(())
    }
}

#[routes]
impl UsersController {
    #[get("/:id")]
    #[guard(AuthGuard)]
    #[pipe(ValidationPipe)]
    #[openapi(
        summary = "Find user by ID",
        tags = ["users", "read"],
        status = 201,
        request = CreateUserDto,
        response = UserDto
    )]
    async fn find_one(&self) {}

    #[post("/")]
    #[validate]
    async fn create(&self) {}
}

fn main() {
    let routes = UsersController::routes();
    assert_eq!(routes.len(), 2);
    assert_eq!(routes[0].method(), "GET");
    assert_eq!(routes[0].path(), "/:id");
    assert_eq!(routes[0].summary(), Some("Find user by ID"));
    assert_eq!(routes[0].tags(), ["users", "read"]);
    assert_eq!(routes[0].response_status(), Some(StatusCode::CREATED));
    assert_eq!(routes[0].request_schema(), Some("CreateUserDto"));
    assert_eq!(routes[0].response_schema(), Some("UserDto"));
    assert_eq!(routes[0].guards(), ["AuthGuard"]);
    assert_eq!(routes[0].pipes(), ["ValidationPipe"]);
    assert!(!routes[0].validates());
    assert!(routes[0].request_schema_registrar().is_some());
    let mut route_request_schemas = Vec::<(String, serde_json::Value)>::new();
    let Some(register_request_schema) = routes[0].request_schema_registrar() else {
        panic!("expected request schema registrar for route");
    };
    register_request_schema(&mut route_request_schemas)
        .expect("request schema registration should succeed");
    assert!(
        route_request_schemas
            .iter()
            .any(|(name, _)| name.as_str() == "CreateUserDto")
    );
    assert!(routes[0].response_schema_registrar().is_some());
    let mut route_response_schemas = Vec::<(String, serde_json::Value)>::new();
    let Some(register_response_schema) = routes[0].response_schema_registrar() else {
        panic!("expected response schema registrar for route");
    };
    register_response_schema(&mut route_response_schemas)
        .expect("response schema registration should succeed");
    assert!(
        route_response_schemas
            .iter()
            .any(|(name, _)| name.as_str() == "UserDto")
    );
    assert_eq!(routes[1].method(), "POST");
    assert_eq!(routes[1].path(), "/");
    assert_eq!(routes[1].summary(), None);
    assert!(routes[1].tags().is_empty());
    assert_eq!(routes[1].response_status(), None);
    assert_eq!(routes[1].request_schema(), None);
    assert_eq!(routes[1].response_schema(), None);
    assert!(routes[1].guards().is_empty());
    assert!(routes[1].pipes().is_empty());
    assert!(routes[1].validates());
}
