// FastAPI's `APIRouter` + `include_router`: routes written away from any
// application and folded into one later, so a large API can be split by feature
// without every piece having to know the app it ends up on — and so the
// attributes a whole group shares (a prefix, tags, a security requirement) are
// stated once at the join rather than copied onto every route.
//
// `App::mount` is the other composition primitive and answers a different
// question: a mount stays a separate application behind a prefix, with its own
// middleware and lifespan, while an included router's routes become the parent's
// own — indistinguishable, afterwards, from routes registered on it directly.

///|
/// A collection of routes built away from any application and folded into one
/// with `App::include_router` (← FastAPI's `APIRouter`). It carries the registration
/// surface of an `App` and nothing else: middleware, mounts, security schemes,
/// documentation and lifespan belong to the application that includes it.
pub struct Router {
  routes : Array[Route]
  ws_routes : Array[@socket.WsRoute]
}

///|
/// An empty router. The prefix and the attributes its routes share are given at
/// `App::include_router` rather than here, so one router can be included twice
/// — under a second prefix, or on another app with different tags.
pub fn Router::new() -> Router {
  { routes: [], ws_routes: [], }
}

///|
/// Register a route on the router for an explicit method. Takes what
/// `App::route` takes and means the same by it; the route reaches an application
/// when the router is included.
pub fn Router::route(
  self : Router,
  verb : Method,
  path : String,
  handler : ApiHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.routes.push({
    verb,
    path,
    run: (ctx, _bg) => Buffered(handler(ctx)),
    summary,
    description,
    tags,
    deprecated,
    operation_id,
    status_code,
    responses,
    name,
    openapi_extra,
    endpoint,
    security,
    dependencies,
    include_in_schema,
    validate,
  })
}

///|
/// Register a background-aware route on the router — `App::route_bg`, deferred
/// to whichever application includes it.
pub fn Router::route_bg(
  self : Router,
  verb : Method,
  path : String,
  handler : BackgroundHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.routes.push({
    verb,
    path,
    run: (ctx, bg) => Buffered(handler(ctx, bg)),
    summary,
    description,
    tags,
    deprecated,
    operation_id,
    status_code,
    responses,
    name,
    openapi_extra,
    endpoint,
    security,
    dependencies,
    include_in_schema,
    validate,
  })
}

///|
/// Register a streaming `GET` route on the router — `App::stream`, deferred to
/// whichever application includes it.
pub fn Router::stream(
  self : Router,
  path : String,
  handler : StreamHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.routes.push({
    verb: Get,
    path,
    run: (ctx, _bg) => Streamed(handler(ctx)),
    summary,
    description,
    tags,
    deprecated,
    operation_id,
    status_code,
    responses,
    name,
    openapi_extra,
    endpoint,
    security,
    dependencies,
    include_in_schema,
    validate,
  })
}

///|
/// Register a `GET` route on the router.
pub fn Router::get(
  self : Router,
  path : String,
  handler : ApiHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.route(
    Get,
    path,
    handler,
    summary~,
    description~,
    tags~,
    deprecated~,
    operation_id~,
    status_code?,
    responses~,
    name~,
    endpoint?,
    security~,
    dependencies~,
    include_in_schema~,
    validate~,
    openapi_extra?,
  )
}

///|
/// Register a `POST` route on the router.
pub fn Router::post(
  self : Router,
  path : String,
  handler : ApiHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.route(
    Post,
    path,
    handler,
    summary~,
    description~,
    tags~,
    deprecated~,
    operation_id~,
    status_code?,
    responses~,
    name~,
    endpoint?,
    security~,
    dependencies~,
    include_in_schema~,
    validate~,
    openapi_extra?,
  )
}

///|
/// Register a `PUT` route on the router.
pub fn Router::put(
  self : Router,
  path : String,
  handler : ApiHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.route(
    Put,
    path,
    handler,
    summary~,
    description~,
    tags~,
    deprecated~,
    operation_id~,
    status_code?,
    responses~,
    name~,
    endpoint?,
    security~,
    dependencies~,
    include_in_schema~,
    validate~,
    openapi_extra?,
  )
}

///|
/// Register a `PATCH` route on the router.
pub fn Router::patch(
  self : Router,
  path : String,
  handler : ApiHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.route(
    Patch,
    path,
    handler,
    summary~,
    description~,
    tags~,
    deprecated~,
    operation_id~,
    status_code?,
    responses~,
    name~,
    endpoint?,
    security~,
    dependencies~,
    include_in_schema~,
    validate~,
    openapi_extra?,
  )
}

///|
/// Register a `DELETE` route on the router.
pub fn Router::delete(
  self : Router,
  path : String,
  handler : ApiHandler,
  summary~ : String = "",
  description~ : String = "",
  tags~ : Array[String] = [],
  deprecated~ : Bool = false,
  operation_id~ : String = "",
  status_code? : Int32,
  responses~ : Array[@endpoint.ResponseSpec] = [],
  name~ : String = "",
  endpoint? : @endpoint.Endpoint,
  security~ : Array[@security.SecurityRequirement] = [],
  dependencies~ : Array[String] = [],
  include_in_schema~ : Bool = true,
  validate~ : Bool = true,
  openapi_extra? : Json,
) -> Unit {
  self.route(
    Delete,
    path,
    handler,
    summary~,
    description~,
    tags~,
    deprecated~,
    operation_id~,
    status_code?,
    responses~,
    name~,
    endpoint?,
    security~,
    dependencies~,
    include_in_schema~,
    validate~,
    openapi_extra?,
  )
}

///|
/// Register a WebSocket route on the router (← `APIRouter.websocket`). It takes
/// the including prefix like any other route; the operation attributes do not
/// apply, since a WebSocket route is not an OpenAPI operation.
pub fn Router::websocket(
  self : Router,
  path : String,
  handler : @socket.WsHandler,
) -> Unit {
  self.ws_routes.push({ path, handler, })
}