///|
/// The continuation function passed to middleware, calling the next handler in the chain.
pub type MiddlewareNext = async () -> &Responder noraise

///|
/// A middleware function that receives a request event and a next continuation, and returns a response.
pub type Middleware = async (MocketEvent, MiddlewareNext) -> &Responder noraise

///|
fn normalize_middleware_base_path(base_path : String) -> String {
  if base_path == "" {
    ""
  } else if base_path.length() > 1 && base_path[base_path.length() - 1:] == "/" {
    base_path[:base_path.length() - 1].to_string()
  } else {
    base_path
  }
}

///|
/// Adds a middleware to the app, optionally scoped to a base path prefix.
pub fn Mocket::use_middleware(
  self : Mocket,
  middleware : Middleware,
  base_path? : String,
) -> Unit {
  let base_path = normalize_middleware_base_path(
    base_path.unwrap_or(self.base_path),
  )
  // Store middleware together with path scope
  self.middlewares.push((base_path, middleware))
}

///|
// Execute middleware chain with path matching (onion model)
async fn execute_middlewares(
  middlewares : Array[(String, Middleware)],
  event : MocketEvent,
  final_handler : HttpHandler,
) -> &Responder noraise {
  // Fast path: no middleware registered
  if middlewares.is_empty() {
    return final_handler(event)
  }
  // Filter middlewares matching the request path
  let matched_middlewares = []
  let request_path = event.req.path()
  for mw in middlewares {
    let (base_path, middleware) = mw
    // Empty base_path means global middleware;
    // otherwise check if the request path matches the scope
    if base_path == "" || @mhttp.path_scope_matches(base_path, request_path) {
      matched_middlewares.push(middleware)
    }
  }
  // Fast path: no middleware matched this path
  if matched_middlewares.is_empty() {
    return final_handler(event)
  }
  // Build middleware chain recursively (onion model)
  execute_middleware_chain(matched_middlewares, 0, event, final_handler)
}

///|
// Recursively execute middleware chain
async fn execute_middleware_chain(
  middlewares : Array[Middleware],
  index : Int,
  event : MocketEvent,
  final_handler : HttpHandler,
) -> &Responder noraise {
  if index >= middlewares.length() {
    // All middlewares executed: call the final handler
    final_handler(event)
  } else {
    // Execute current middleware
    let current_middleware = middlewares[index]
    let next = async fn() noraise {
      execute_middleware_chain(middlewares, index + 1, event, final_handler)
    }
    current_middleware(event, next)
  }
}