//! Application facade

mod registry;
mod route;

pub use registry::Registry;
pub use route::Route;

use crate::core::{CoreError, Handler, Method, Middleware};
use crate::domain::{Request, Response};
use crate::pipeline::{Dispatcher, MiddlewareOptions};
use parking_lot::Mutex;
use std::sync::Arc;

/// Application - Main facade
///
/// The App uses a Registry to collect route and middleware registrations,
/// then builds an immutable Dispatcher on first request or when explicitly built.
///
/// Uses interior mutability (Mutex) to allow lazy dispatcher building while
/// being shareable across threads via Arc.
///
/// # Generic Parameter
///
/// - `E`: Environment type. For shared state, wrap in `Arc<YourEnv>`.
///
/// # Examples
///
/// ```rust,ignore
/// // Simple app with no state
/// let app = App::new();
///
/// // App with owned state (not shared)
/// struct Config { port: u16 }
/// let app = App::with_env(Config { port: 3000 });
///
/// // App with shared state (recommended for production)
/// let app = App::with_env(Arc::new(AppEnv::new()));
/// ```
pub struct App<E = ()> {
    inner: Mutex<AppInner<E>>,
    env: E,
}

struct AppInner<E> {
    registry: Registry<E>,
    dispatcher: Option<Arc<Dispatcher<E>>>,
}

impl App<()> {
    /// Create new app with no environment
    pub fn new() -> Self {
        Self::with_env(())
    }
}

impl<E> App<E>
where
    E: Send + Sync + 'static + Clone,
{
    /// Create new app with environment
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// use std::sync::Arc;
    ///
    /// struct AppEnv {
    ///     db: Database,
    /// }
    ///
    /// // Recommended: wrap in Arc for shared state
    /// let app = App::with_env(Arc::new(AppEnv::new()));
    /// ```
    pub fn with_env(env: E) -> Self {
        Self {
            inner: Mutex::new(AppInner {
                registry: Registry::new(),
                dispatcher: None,
            }),
            env,
        }
    }

    /// Get reference to environment
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// let env = app.env();
    /// ```
    pub fn env(&self) -> &E {
        &self.env
    }

    /// Build the dispatcher from the registry
    ///
    /// This consumes the registry and creates an immutable Dispatcher.
    /// Called automatically by `listen()` before starting the server.
    ///
    /// This method is idempotent - calling it multiple times has no effect
    /// after the first call.
    fn build_dispatcher(&self) {
        let mut inner = self.inner.lock();

        if inner.dispatcher.is_some() {
            return; // Already built
        }

        let mut dispatcher = Dispatcher::new(self.env.clone());

        // Take the registry contents
        let registry = std::mem::take(&mut inner.registry);
        let (routes, middleware) = registry.into_parts();

        // Register all middleware first (order matters for PostRouting)
        for entry in middleware {
            if let Err(e) = dispatcher.register_middleware(
                entry.phase(),
                entry.middleware(),
                entry.options().cloned(),
            ) {
                panic!("Failed to register middleware: {}", e);
            }
        }

        // Register all routes (this pre-computes middleware chains)
        for entry in routes {
            let method = entry.method().clone();
            let path = entry.path().to_string();
            let handler = entry.handler();

            if let Err(e) = dispatcher.register_route(method.clone(), &path, handler) {
                panic!(
                    "Failed to register route {} {}: {}",
                    method.as_str(),
                    path,
                    e
                );
            }
        }

        inner.dispatcher = Some(Arc::new(dispatcher));
    }

    // Route registration

    /// Create a route builder for chaining multiple methods
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let mut app = App::new();
    /// app.route("/users")
    ///     .get(async |c: Context| c.text("Get users"))
    ///     .post(async |c: Context| c.text("Create user"));
    /// ```
    pub fn route(&self, path: impl Into<String>) -> Route<'_, E> {
        Route::new(self, path)
    }

    /// Register route (internal helper for Route builder)
    pub(crate) fn register_route(&self, method: Method, path: &str, handler: impl Handler<E>) {
        let mut inner = self.inner.lock();
        inner.registry.register_route(method, path, handler);
    }

    /// Register GET route
    pub fn get(&self, path: &str, handler: impl Handler<E>) -> &Self {
        self.register_route(Method::GET, path, handler);
        self
    }

    /// Register POST route
    pub fn post(&self, path: &str, handler: impl Handler<E>) -> &Self {
        self.register_route(Method::POST, path, handler);
        self
    }

    /// Register PUT route
    pub fn put(&self, path: &str, handler: impl Handler<E>) -> &Self {
        self.register_route(Method::PUT, path, handler);
        self
    }

    /// Register DELETE route
    pub fn delete(&self, path: &str, handler: impl Handler<E>) -> &Self {
        self.register_route(Method::DELETE, path, handler);
        self
    }

    /// Register PATCH route
    pub fn patch(&self, path: &str, handler: impl Handler<E>) -> &Self {
        self.register_route(Method::PATCH, path, handler);
        self
    }

    /// Mount another App instance at a specific path (nested routing)
    ///
    /// This allows you to organize routes into separate App instances and mount them
    /// at different base paths, similar to Hono's `route()` method.
    ///
    /// All routes and middleware from the nested app will be prefixed with the given path.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// // Create a sub-app for API routes
    /// let api = App::new();
    /// api.get("/users", |ctx: Context| async move {
    ///     ctx.json(&serde_json::json!({"users": []}))
    /// });
    /// api.get("/posts", |ctx: Context| async move {
    ///     ctx.json(&serde_json::json!({"posts": []}))
    /// });
    ///
    /// // Mount the API routes under /api
    /// let app = App::new();
    /// app.mount("/api", api);
    ///
    /// // GET /api/users -> {"users": []}
    /// // GET /api/posts -> {"posts": []}
    /// ```
    ///
    /// ## Nested routing with multiple levels
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let v1 = App::new();
    /// v1.get("/users", |ctx: Context| async move {
    ///     ctx.json(&serde_json::json!({"version": "v1", "users": []}))
    /// });
    ///
    /// let api = App::new();
    /// api.mount("/v1", v1);
    ///
    /// let app = App::new();
    /// app.mount("/api", api);
    ///
    /// // GET /api/v1/users -> {"version": "v1", "users": []}
    /// ```
    pub fn mount(&self, path: &str, sub_app: App<E>) -> &Self {
        use crate::routing::join_paths;

        let mut inner = self.inner.lock();
        let mut sub_inner = sub_app.inner.lock();

        // Normalize the base path (remove trailing slash)
        let base_path = path.trim_end_matches('/');

        // Take the sub-app's registry
        let sub_registry = std::mem::take(&mut sub_inner.registry);
        let (sub_routes, sub_middleware) = sub_registry.into_parts();

        // Register all routes from sub-app with prefixed path
        for entry in sub_routes {
            let full_path = join_paths(base_path, entry.path());
            inner
                .registry
                .register_route(entry.method().clone(), &full_path, entry.handler());
        }

        // Register all middleware from sub-app with prefixed pattern
        for entry in sub_middleware {
            let adjusted_options = match entry.options() {
                Some(opts) => {
                    let prefixed_pattern = match opts.pattern() {
                        None if base_path.is_empty() => None,
                        None => Some(format!("{}/*", base_path)),
                        Some(pattern) => Some(join_paths(base_path, pattern)),
                    };

                    let mut new_opts = MiddlewareOptions::new();
                    if let Some(method) = opts.method() {
                        new_opts = new_opts.for_method(method.clone());
                    }
                    if let Some(pattern) = prefixed_pattern {
                        new_opts = new_opts.on(pattern);
                    }
                    Some(new_opts)
                }
                None if base_path.is_empty() => None,
                None => {
                    // BUGFIX: Global middleware from sub-apps should match BOTH:
                    // 1. The exact mount point (e.g., /api/sessions)
                    // 2. Paths under the mount point (e.g., /api/sessions/*)
                    //
                    // Since matchit patterns can't express "match A or B", we need to
                    // register the middleware twice: once for the exact path, once for wildcard.
                    //
                    // First registration: exact mount point
                    inner.registry.register_middleware(
                        entry.phase(),
                        entry.middleware(),
                        Some(MiddlewareOptions::new().on(base_path.to_string())),
                    );

                    // Second registration: wildcard pattern
                    // (will be registered below after this match expression)
                    Some(MiddlewareOptions::new().on(format!("{}/*", base_path)))
                }
            };

            inner
                .registry
                .register_middleware(entry.phase(), entry.middleware(), adjusted_options);
        }

        drop(inner);
        self
    }

    /// Register a WebSocket route
    ///
    /// Registers a `GET` route at `path` that performs the WebSocket upgrade
    /// handshake automatically. The provided handler is called with a
    /// connected [`WebSocket`] once the upgrade completes; user code does
    /// not need to call [`WebSocketUpgrade`] directly.
    ///
    /// The handler runs in a spawned task — it owns the socket for the
    /// lifetime of the connection. Returning from the handler closes the
    /// connection cleanly.
    ///
    /// [`WebSocket`]: crate::upgrade::websocket::WebSocket
    /// [`WebSocketUpgrade`]: crate::upgrade::websocket::WebSocketUpgrade
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use ruxno::prelude::*;
    /// use ruxno::upgrade::websocket::{Message, WebSocket};
    ///
    /// let app = App::new();
    /// app.ws("/ws", |_ctx, mut socket: WebSocket| async move {
    ///     while let Some(Ok(msg)) = socket.recv().await {
    ///         if let Message::Text(t) = msg {
    ///             socket.send_text(t.into_string()).await.ok();
    ///         }
    ///     }
    /// });
    /// ```
    pub fn ws<F, Fut>(&self, path: &str, handler: F) -> &Self
    where
        F: Fn(crate::domain::Context<E>, crate::upgrade::websocket::WebSocket) -> Fut
            + Send
            + Sync
            + Clone
            + 'static,
        Fut: std::future::Future<Output = ()> + Send + 'static,
    {
        self.register_route(Method::GET, path, move |ctx: crate::domain::Context<E>| {
            let handler = handler.clone();
            async move {
                // Capture a clone of the context for the handler closure to use,
                // then drive the upgrade with the original context.
                let handler_ctx = ctx.clone();
                let upgrade = crate::upgrade::websocket::WebSocketUpgrade::new(ctx);
                upgrade
                    .upgrade(move |socket| async move { handler(handler_ctx, socket).await })
                    .await
            }
        });
        self
    }

    /// Register a Server-Sent Events route
    ///
    /// Registers a `GET` route at `path` that returns an
    /// `text/event-stream` streaming response. The provided handler is
    /// spawned as a background task with an [`SseSender`]; events written
    /// through the sender are forwarded to the client until either side
    /// disconnects.
    ///
    /// A 15s keep-alive comment is enabled by default to keep the
    /// connection open through buffering proxies. Use [`Self::sse_with`]
    /// for full control.
    ///
    /// [`SseSender`]: crate::upgrade::sse::SseSender
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use ruxno::prelude::*;
    /// use ruxno::upgrade::sse::SseSender;
    /// use std::time::Duration;
    ///
    /// let app = App::new();
    /// app.sse("/events", |_ctx, sender: SseSender| async move {
    ///     for i in 0..10 {
    ///         if sender.send_data(format!("tick {i}")).await.is_err() {
    ///             break;
    ///         }
    ///         tokio::time::sleep(Duration::from_secs(1)).await;
    ///     }
    /// });
    /// ```
    pub fn sse<F, Fut>(&self, path: &str, handler: F) -> &Self
    where
        F: Fn(crate::domain::Context<E>, crate::upgrade::sse::SseSender) -> Fut
            + Send
            + Sync
            + Clone
            + 'static,
        Fut: std::future::Future<Output = ()> + Send + 'static,
    {
        self.sse_with(path, crate::upgrade::sse::KeepAlive::default(), handler)
    }

    /// Register a Server-Sent Events route with a custom keep-alive.
    ///
    /// Same as [`Self::sse`] but lets the caller pick the keep-alive
    /// configuration (including disabling it entirely by constructing a
    /// `KeepAlive` with a very large interval).
    pub fn sse_with<F, Fut>(
        &self,
        path: &str,
        keep_alive: crate::upgrade::sse::KeepAlive,
        handler: F,
    ) -> &Self
    where
        F: Fn(crate::domain::Context<E>, crate::upgrade::sse::SseSender) -> Fut
            + Send
            + Sync
            + Clone
            + 'static,
        Fut: std::future::Future<Output = ()> + Send + 'static,
    {
        self.register_route(Method::GET, path, move |ctx: crate::domain::Context<E>| {
            let handler = handler.clone();
            async move {
                let stream = crate::upgrade::sse::SseStream::new().with_keep_alive(keep_alive);
                let sender = stream.sender();
                tokio::spawn(async move {
                    handler(ctx, sender).await;
                });
                Ok(stream.into_response())
            }
        });
        self
    }

    // Middleware registration

    /// Register pre-routing middleware (runs before routing)
    ///
    /// Pre-routing middleware executes before route matching occurs.
    /// Use this for:
    /// - CORS preflight requests
    /// - Health checks that should bypass routing
    /// - Early request rejection (rate limiting, IP blocking)
    /// - Global request logging
    ///
    /// **Important**: Pre-routing middleware CANNOT access route parameters
    /// because routing hasn't happened yet.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let mut app = App::new();
    ///
    /// // CORS preflight middleware
    /// app.use_before_routing(|ctx: Context, next: Next| async move {
    ///     if ctx.req.method() == &Method::OPTIONS {
    ///         return Ok(Response::new().with_status_code(204));
    ///     }
    ///     next.run(ctx).await
    /// });
    /// ```
    pub fn use_before_routing(&self, middleware: impl Middleware<E>) -> &Self {
        use crate::pipeline::MiddlewarePhase;
        let mut inner = self.inner.lock();
        inner
            .registry
            .register_middleware(MiddlewarePhase::PreRouting, middleware, None);
        self
    }

    /// Register pre-routing middleware with path filter
    ///
    /// Pre-routing middleware with path filter runs before routing,
    /// but only for requests matching the specified pattern.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let mut app = App::new();
    ///
    /// // Health check that bypasses routing
    /// app.use_before_routing_on("/health", |ctx: Context, _next: Next| async move {
    ///     Ok(Response::json(&serde_json::json!({"status": "ok"})))
    /// });
    /// ```
    pub fn use_before_routing_on(&self, pattern: &str, middleware: impl Middleware<E>) -> &Self {
        use crate::pipeline::MiddlewarePhase;
        let opts = MiddlewareOptions::new().on(pattern);
        let mut inner = self.inner.lock();
        inner
            .registry
            .register_middleware(MiddlewarePhase::PreRouting, middleware, Some(opts));
        self
    }

    /// Register post-routing middleware (runs after routing)
    ///
    /// Post-routing middleware executes after route matching.
    /// Use this for:
    /// - Authentication and authorization
    /// - Request validation
    /// - Logging with route context
    /// - Response transformation
    ///
    /// **Benefit**: Post-routing middleware HAS access to route parameters
    /// extracted during routing.
    ///
    /// Patterns support:
    /// - Exact match: `/api/users`
    /// - Parameters: `/api/users/:id`
    /// - Wildcards: `/api/*` (matches all paths under `/api`)
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let mut app = App::new();
    ///
    /// // Global middleware (no pattern)
    /// app.r#use(|ctx: Context, next: Next| async move {
    ///     println!("Request: {}", ctx.req.path());
    ///     next.run(ctx).await
    /// });
    ///
    /// // Path-specific middleware
    /// app.r#use("/api/*").with(|ctx: Context, next: Next| async move {
    ///     // Auth middleware for /api routes
    ///     next.run(ctx).await
    /// });
    /// ```
    pub fn r#use(&self, middleware: impl Middleware<E>) -> &Self {
        use crate::pipeline::MiddlewarePhase;
        let mut inner = self.inner.lock();
        inner
            .registry
            .register_middleware(MiddlewarePhase::PostRouting, middleware, None);
        self
    }

    /// Register post-routing middleware with path filter
    ///
    /// Post-routing middleware with path filter runs after routing,
    /// but only for requests matching the specified pattern.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let mut app = App::new();
    ///
    /// // Auth middleware for API routes
    /// app.use_on("/api/*", |ctx: Context, next: Next| async move {
    ///     // Check authentication
    ///     next.run(ctx).await
    /// });
    /// ```
    pub fn use_on(&self, pattern: &str, middleware: impl Middleware<E>) -> &Self {
        use crate::pipeline::MiddlewarePhase;
        let opts = MiddlewareOptions::new().on(pattern);
        let mut inner = self.inner.lock();
        inner
            .registry
            .register_middleware(MiddlewarePhase::PostRouting, middleware, Some(opts));
        self
    }

    /// Register method + path-specific middleware
    ///
    /// Middleware applies only to routes matching both the method and pattern.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// let mut app = App::new();
    ///
    /// // Method + path-specific middleware
    /// app.on(Method::POST, "/api/*", |ctx: Context, next: Next| async move {
    ///     // Validation middleware for POST /api routes
    ///     next.run(ctx).await
    /// });
    ///
    /// // Routes registered after will include the middleware if they match
    /// app.post("/api/users", |ctx: Context| async move {
    ///     Ok(ctx.text("Create user"))  // Includes validation middleware
    /// });
    ///
    /// app.get("/api/users", |ctx: Context| async move {
    ///     Ok(ctx.text("Get users"))  // No validation middleware (GET, not POST)
    /// });
    /// ```
    pub fn on(&self, method: Method, pattern: &str, middleware: impl Middleware<E>) -> &Self {
        use crate::pipeline::MiddlewarePhase;
        let opts = MiddlewareOptions::new().for_method(method).on(pattern);
        let mut inner = self.inner.lock();
        inner
            .registry
            .register_middleware(MiddlewarePhase::PostRouting, middleware, Some(opts));
        self
    }

    // Server

    /// Start listening
    ///
    /// Creates a server and starts listening for HTTP connections.
    /// The server will run until Ctrl+C is pressed.
    ///
    /// This method builds the dispatcher from the registry before starting the server,
    /// ensuring no delay on the first request.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// use ruxno_clean::prelude::*;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     let app = App::new();
    ///     app.get("/", |ctx: Context| async move {
    ///         Ok(ctx.text("Hello, World!"))
    ///     });
    ///
    ///     app.listen("127.0.0.1:3000").await?;
    ///     Ok(())
    /// }
    /// ```
    pub async fn listen(self, addr: &str) -> Result<(), CoreError> {
        // Build the dispatcher before starting the server
        self.build_dispatcher();

        use crate::server::Server;
        let server = Server::new(self);
        server.listen(addr).await
    }

    /// Dispatch request (internal)
    pub(crate) async fn dispatch(&self, req: Request) -> Result<Response, CoreError> {
        // Build the dispatcher lazily on first dispatch if not already built.
        // This allows tests to call dispatch() directly without going through listen().
        self.build_dispatcher();

        // Clone the dispatcher Arc without holding the lock across await
        let dispatcher = {
            let inner = self.inner.lock();
            Arc::clone(
                inner
                    .dispatcher
                    .as_ref()
                    .expect("Dispatcher should be built before dispatching requests"),
            )
        };

        // Dispatch through the immutable dispatcher
        dispatcher.dispatch(req).await
    }
}

impl<E> Default for App<E>
where
    E: Default + Send + Sync + 'static + Clone,
{
    fn default() -> Self {
        Self::with_env(E::default())
    }
}

#[cfg(test)]
mod tests {
    use http::Version;

    use super::*;

    #[test]
    fn test_app_with_env() {
        #[derive(Debug, Clone)]
        struct MyEnv {
            value: i32,
        }

        let env = MyEnv { value: 42 };
        let app = App::with_env(env);
        assert_eq!(app.env().value, 42);
    }

    #[test]
    fn test_app_with_custom_env_default() {
        #[derive(Debug, Clone, Default)]
        struct Config {
            port: u16,
        }

        let app = App::<Config>::default();
        assert_eq!(app.env().port, 0);
    }

    #[test]
    fn test_app_use() {
        let app = App::new();
        // Should not panic - using PostRouting phase
        app.r#use(|_ctx: crate::domain::Context, next: crate::core::Next| async move {
            next.run(_ctx).await
        });
    }

    #[test]
    fn test_app_use_before_routing() {
        let app = App::new();
        // Should not panic - using PreRouting phase
        app.use_before_routing(|_ctx: crate::domain::Context, next: crate::core::Next| async move {
            next.run(_ctx).await
        });
    }

    #[test]
    fn test_app_on() {
        let app = App::new();
        // Should not panic - using PostRouting phase
        app.on(
            Method::POST,
            "/api/*",
            |_ctx: crate::domain::Context, next: crate::core::Next| async move {
                next.run(_ctx).await
            },
        );
    }

    #[tokio::test]
    async fn test_app_pattern_middleware_integration() {
        use crate::core::Middleware;
        use crate::domain::Response;
        use async_trait::async_trait;

        struct TestMiddleware {
            header: String,
        }

        #[async_trait]
        impl Middleware<()> for TestMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<()>,
                next: crate::core::Next<()>,
            ) -> Result<Response, CoreError> {
                let mut response = next.run(ctx).await?;
                response.headers_mut().set("X-Test", &self.header).ok();
                Ok(response)
            }
        }

        let app = App::new();

        // Register pre-routing middleware using use_before_routing()
        app.use_before_routing(TestMiddleware {
            header: "global".to_string(),
        });

        // Register routes
        app.get("/api/users", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("users"))
        });

        app.get("/public", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("public"))
        });

        // Test /api/users - should have middleware header
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/users".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        assert_eq!(response.headers().get("X-Test").unwrap(), "global");

        // Test /public - should also have middleware header (global pre-routing)
        let req = crate::domain::Request::new(
            Method::GET,
            "/public".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        assert_eq!(response.headers().get("X-Test").unwrap(), "global");
    }

    #[tokio::test]
    async fn test_app_method_pattern_middleware_integration() {
        use crate::core::Middleware;
        use crate::domain::Response;
        use async_trait::async_trait;

        struct TestMiddleware {
            header: String,
        }

        #[async_trait]
        impl Middleware<()> for TestMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<()>,
                next: crate::core::Next<()>,
            ) -> Result<Response, CoreError> {
                let mut response = next.run(ctx).await?;
                response.headers_mut().set("X-Validated", &self.header).ok();
                Ok(response)
            }
        }

        let app = App::new();

        // Register method + pattern-specific middleware using on()
        app.on(
            Method::POST,
            "/api/*",
            TestMiddleware {
                header: "true".to_string(),
            },
        );

        // Register routes
        app.post("/api/users", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("create"))
        });

        app.get("/api/users", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("get"))
        });

        // Test POST /api/users - should have middleware header
        let req = crate::domain::Request::new(
            Method::POST,
            "/api/users".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        assert_eq!(response.headers().get("X-Validated").unwrap(), "true");

        // Test GET /api/users - should NOT have middleware header (wrong method)
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/users".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        assert!(response.headers().get("X-Validated").is_none());
    }

    #[tokio::test]
    async fn test_nested_route_basic() {
        use crate::domain::ResponseBody;

        // Create a sub-app for API routes
        let api = App::new();
        api.get("/users", |ctx: crate::domain::Context<()>| async move {
            ctx.json(&serde_json::json!({"users": []}))
        });
        api.get("/posts", |ctx: crate::domain::Context<()>| async move {
            ctx.json(&serde_json::json!({"posts": []}))
        });

        // Mount the API routes under /api
        let app = App::new();
        app.mount("/api", api);

        // Test GET /api/users
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/users".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        match response.body() {
            ResponseBody::Bytes(bytes) => {
                assert!(String::from_utf8_lossy(bytes).contains("users"));
            }
            _ => panic!("Expected Bytes body"),
        }

        // Test GET /api/posts
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/posts".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        match response.body() {
            ResponseBody::Bytes(bytes) => {
                assert!(String::from_utf8_lossy(bytes).contains("posts"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[tokio::test]
    async fn test_nested_route_multiple_levels() {
        use crate::domain::ResponseBody;

        // Create v1 API
        let v1 = App::new();
        v1.get("/users", |ctx: crate::domain::Context<()>| async move {
            ctx.json(&serde_json::json!({"version": "v1", "users": []}))
        });

        // Create API app and nest v1
        let api = App::new();
        api.mount("/v1", v1);

        // Create main app and nest API
        let app = App::new();
        app.mount("/api", api);

        // Test GET /api/v1/users
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/v1/users".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        match response.body() {
            ResponseBody::Bytes(bytes) => {
                let json_str = String::from_utf8_lossy(bytes);
                assert!(json_str.contains("v1"));
                assert!(json_str.contains("users"));
            }
            _ => panic!("Expected Bytes body"),
        }
    }

    #[tokio::test]
    async fn test_nested_route_with_middleware() {
        use crate::core::Middleware;
        use crate::domain::Response;
        use async_trait::async_trait;

        struct AddHeaderMiddleware {
            name: String,
            value: String,
        }

        #[async_trait]
        impl Middleware<()> for AddHeaderMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<()>,
                next: crate::core::Next<()>,
            ) -> Result<Response, CoreError> {
                let mut response = next.run(ctx).await?;
                response.headers_mut().set(&self.name, &self.value).ok();
                Ok(response)
            }
        }

        // Create sub-app with middleware
        let api = App::new();
        api.r#use(AddHeaderMiddleware {
            name: "X-API".to_string(),
            value: "protected".to_string(),
        });
        api.get("/users", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("users"))
        });

        // Mount under /api
        let app = App::new();
        app.mount("/api", api);

        // Test that middleware is applied
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/users".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let response = app.dispatch(req).await.unwrap();
        assert_eq!(response.headers().get("X-API").unwrap(), "protected");
    }

    /// Test for Issue #1: Middleware leaking from sub-apps to parent routes
    ///
    /// This test verifies that middleware applied to a sub-app using `.r#use()`
    /// ONLY affects routes within that sub-app and does NOT leak to parent app routes.
    #[tokio::test]
    async fn test_subapp_middleware_isolation() {
        use crate::core::{Middleware, StatusCode};
        use crate::domain::Response;
        use async_trait::async_trait;

        // Middleware that rejects requests without Authorization header
        struct AuthMiddleware;

        #[async_trait]
        impl Middleware<()> for AuthMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<()>,
                next: crate::core::Next<()>,
            ) -> Result<Response, CoreError> {
                // Check for Authorization header
                if ctx.req.header("Authorization").is_none() {
                    return Ok(Response::new()
                        .with_status_code(StatusCode::UNAUTHORIZED)
                        .with_body("Unauthorized"));
                }
                next.run(ctx).await
            }
        }

        // Create main app with a public route
        let app = App::new();
        app.get("/public", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("Public endpoint - no auth required"))
        });

        // Create sub-app with auth middleware
        let api = App::new();
        api.r#use(AuthMiddleware); // Should only affect /api/* routes
        api.get("/protected", |ctx: crate::domain::Context<()>| async move {
            Ok(ctx.text("Protected endpoint"))
        });

        // Mount sub-app
        app.mount("/api", api);

        // Test 1: Public route should work WITHOUT Authorization header
        let public_req = crate::domain::Request::new(
            Method::GET,
            "/public".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let public_response = app.dispatch(public_req).await.unwrap();

        // ASSERTION: Public route should return 200 OK, not 401 Unauthorized
        assert_eq!(
            public_response.status(),
            StatusCode::OK,
            "Public route should not be affected by sub-app middleware"
        );

        // Test 2: Protected route should FAIL WITHOUT Authorization header
        let protected_req_no_auth = crate::domain::Request::new(
            Method::GET,
            "/api/protected".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let protected_response_no_auth = app.dispatch(protected_req_no_auth).await.unwrap();

        assert_eq!(
            protected_response_no_auth.status(),
            StatusCode::UNAUTHORIZED,
            "Protected route should require authorization"
        );

        // Test 3: Protected route should work WITH Authorization header
        let mut headers = crate::http::Headers::new();
        headers.set("Authorization", "Bearer token").ok();
        let protected_req_with_auth = crate::domain::Request::new(
            Method::GET,
            "/api/protected".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            headers,
            bytes::Bytes::new(),
        );
        let protected_response_with_auth = app.dispatch(protected_req_with_auth).await.unwrap();

        assert_eq!(
            protected_response_with_auth.status(),
            StatusCode::OK,
            "Protected route should work with authorization"
        );
    }

    /// Test for Issue #1: Middleware leaking with `App::with_env()` pattern
    ///
    /// This variant tests the exact scenario from the bug report: creating sub-apps
    /// with `App::with_env(parent.env().clone())` and verifying middleware isolation.
    #[tokio::test]
    async fn test_subapp_middleware_isolation_with_shared_env() {
        use crate::core::{Middleware, StatusCode};
        use crate::domain::Response;
        use async_trait::async_trait;

        #[derive(Clone)]
        struct AppEnv {}

        // Middleware that rejects requests without Authorization header
        struct AuthMiddleware;

        #[async_trait]
        impl Middleware<AppEnv> for AuthMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<AppEnv>,
                next: crate::core::Next<AppEnv>,
            ) -> Result<Response, CoreError> {
                // Check for Authorization header
                if ctx.req.header("Authorization").is_none() {
                    return Ok(Response::new()
                        .with_status_code(StatusCode::UNAUTHORIZED)
                        .with_body("Unauthorized"));
                }
                next.run(ctx).await
            }
        }

        // Create main app with environment
        let env = AppEnv {};
        let app = App::with_env(env);

        // Add public route BEFORE creating sub-app
        app.get(
            "/public",
            |ctx: crate::domain::Context<AppEnv>| async move {
                Ok(ctx.text("Public endpoint - no auth required"))
            },
        );

        // Create sub-app with shared environment (exact pattern from bug report)
        let api = App::with_env(app.env().clone());
        api.r#use(AuthMiddleware); // Should only affect /api/* routes
        api.get(
            "/protected",
            |ctx: crate::domain::Context<AppEnv>| async move { Ok(ctx.text("Protected endpoint")) },
        );

        // Mount sub-app
        app.mount("/api", api);

        // Test 1: Public route should work WITHOUT Authorization header
        let public_req = crate::domain::Request::new(
            Method::GET,
            "/public".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let public_response = app.dispatch(public_req).await.unwrap();

        // ASSERTION: Public route should return 200 OK, not 401 Unauthorized
        assert_eq!(
            public_response.status(),
            StatusCode::OK,
            "Public route should not be affected by sub-app middleware (with shared env)"
        );

        // Test 2: Protected route should FAIL WITHOUT Authorization header
        let protected_req_no_auth = crate::domain::Request::new(
            Method::GET,
            "/api/protected".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let protected_response_no_auth = app.dispatch(protected_req_no_auth).await.unwrap();

        assert_eq!(
            protected_response_no_auth.status(),
            StatusCode::UNAUTHORIZED,
            "Protected route should require authorization"
        );

        // Test 3: Protected route should work WITH Authorization header
        let mut headers = crate::http::Headers::new();
        headers.set("Authorization", "Bearer token").ok();
        let protected_req_with_auth = crate::domain::Request::new(
            Method::GET,
            "/api/protected".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            headers,
            bytes::Bytes::new(),
        );
        let protected_response_with_auth = app.dispatch(protected_req_with_auth).await.unwrap();

        assert_eq!(
            protected_response_with_auth.status(),
            StatusCode::OK,
            "Protected route should work with authorization"
        );
    }

    /// Test for Issue #1: Nested sub-apps with middleware (matching actual bug report pattern)
    ///
    /// Tests the exact pattern from the bug report:
    /// - Main app with public health routes
    /// - API sub-app mounted at /api/v1
    /// - Sessions sub-app with auth middleware mounted under /api/v1/sessions
    ///
    /// The bug report shows jwt_auth middleware was leaking to /health endpoint.
    #[tokio::test]
    async fn test_nested_subapp_middleware_isolation() {
        use crate::core::{Middleware, StatusCode};
        use crate::domain::Response;
        use async_trait::async_trait;

        #[derive(Clone)]
        struct AppEnv;

        // JWT auth middleware
        struct JwtAuthMiddleware;

        #[async_trait]
        impl Middleware<AppEnv> for JwtAuthMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<AppEnv>,
                next: crate::core::Next<AppEnv>,
            ) -> Result<Response, CoreError> {
                // Check for Authorization header
                if ctx.req.header("Authorization").is_none() {
                    return Ok(Response::new()
                        .with_status_code(StatusCode::UNAUTHORIZED)
                        .with_body("Unauthorized - JWT required"));
                }
                next.run(ctx).await
            }
        }

        // Main app
        let app = App::with_env(AppEnv);

        // Public health routes (NO AUTH)
        app.get(
            "/health",
            |ctx: crate::domain::Context<AppEnv>| async move { Ok(ctx.text("healthy")) },
        );
        app.get("/ready", |ctx: crate::domain::Context<AppEnv>| async move {
            Ok(ctx.text("ready"))
        });

        // Create API v1 sub-app
        let api_v1 = App::with_env(app.env().clone());

        // Create sessions sub-app with JWT middleware
        let sessions_app = App::with_env(api_v1.env().clone());
        sessions_app.r#use(JwtAuthMiddleware); // Should ONLY affect /api/v1/sessions/*
        sessions_app.get("/", |ctx: crate::domain::Context<AppEnv>| async move {
            Ok(ctx.text("sessions list"))
        });

        // Mount sessions under /sessions in api_v1
        api_v1.mount("/sessions", sessions_app);

        // Mount api_v1 under /api/v1 in main app
        app.mount("/api/v1", api_v1);

        // Test 1: Health endpoint should work WITHOUT auth
        let health_req = crate::domain::Request::new(
            Method::GET,
            "/health".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let health_response = app.dispatch(health_req).await.unwrap();

        assert_eq!(
            health_response.status(),
            StatusCode::OK,
            "Health endpoint should NOT be affected by sessions JWT middleware"
        );

        // Test 2: Ready endpoint should work WITHOUT auth
        let ready_req = crate::domain::Request::new(
            Method::GET,
            "/ready".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let ready_response = app.dispatch(ready_req).await.unwrap();

        assert_eq!(
            ready_response.status(),
            StatusCode::OK,
            "Ready endpoint should NOT be affected by sessions JWT middleware"
        );

        // Test 3: Sessions endpoint should FAIL without auth
        let sessions_req_no_auth = crate::domain::Request::new(
            Method::GET,
            "/api/v1/sessions".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        let sessions_response_no_auth = app.dispatch(sessions_req_no_auth).await.unwrap();

        assert_eq!(
            sessions_response_no_auth.status(),
            StatusCode::UNAUTHORIZED,
            "Sessions endpoint should require JWT auth"
        );

        // Test 4: Sessions endpoint should work WITH auth
        let mut headers = crate::http::Headers::new();
        headers.set("Authorization", "Bearer jwt_token").ok();
        let sessions_req_with_auth = crate::domain::Request::new(
            Method::GET,
            "/api/v1/sessions".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            headers,
            bytes::Bytes::new(),
        );
        let sessions_response_with_auth = app.dispatch(sessions_req_with_auth).await.unwrap();

        assert_eq!(
            sessions_response_with_auth.status(),
            StatusCode::OK,
            "Sessions endpoint should work with JWT auth"
        );
    }

    /// Debug test to understand nested mounting behavior
    #[tokio::test]
    async fn test_debug_nested_mounting() {
        use crate::core::{Middleware, StatusCode};
        use crate::domain::Response;
        use async_trait::async_trait;

        struct LogMiddleware {
            name: String,
        }

        #[async_trait]
        impl Middleware<()> for LogMiddleware {
            async fn process(
                &self,
                ctx: crate::domain::Context<()>,
                next: crate::core::Next<()>,
            ) -> Result<Response, CoreError> {
                eprintln!(
                    "[{}] Processing: {} {}",
                    self.name,
                    ctx.req.method(),
                    ctx.req.path()
                );
                next.run(ctx).await
            }
        }

        // Create main app
        let app = App::new();
        app.get("/root", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("root"))
        });

        // Create level 1 sub-app
        let level1 = App::new();

        // Create level 2 sub-app with middleware
        let level2 = App::new();
        level2.r#use(LogMiddleware {
            name: "Level2".to_string(),
        });
        level2.get("/endpoint", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("level2 endpoint"))
        });

        // Mount level2 into level1
        level1.mount("/level2", level2);

        // Mount level1 into app
        app.mount("/level1", level1);

        // Test the nested route
        let req = crate::domain::Request::new(
            Method::GET,
            "/level1/level2/endpoint".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );

        eprintln!("\n=== Testing /level1/level2/endpoint ===");
        let response = app.dispatch(req).await.unwrap();
        eprintln!("Response status: {}", response.status());

        // Check if middleware ran
        assert_eq!(response.status(), StatusCode::OK);
    }

    /// Test to ensure the fix doesn't cause false positives
    /// (middleware applying to routes it shouldn't)
    #[tokio::test]
    async fn test_middleware_no_false_positives() {
        use crate::core::{Middleware, StatusCode};
        use crate::domain::Response;
        use async_trait::async_trait;

        struct RejectMiddleware;

        #[async_trait]
        impl Middleware<()> for RejectMiddleware {
            async fn process(
                &self,
                _ctx: crate::domain::Context<()>,
                _next: crate::core::Next<()>,
            ) -> Result<Response, CoreError> {
                // Always reject - if this runs on wrong routes, tests will fail
                Ok(Response::new()
                    .with_status_code(StatusCode::FORBIDDEN)
                    .with_body("Rejected by middleware"))
            }
        }

        let app = App::new();

        // Public routes that should NEVER have middleware
        app.get("/", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("root"))
        });
        app.get("/public", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("public"))
        });
        app.get("/api", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("api root"))
        });
        app.get("/api/other", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("api other"))
        });
        app.get(
            "/api/protected-but-different",
            |_: crate::domain::Context<()>| async move { Ok(Response::text("different route")) },
        );

        // Sub-app with reject middleware mounted at /api/protected
        let protected = App::new();
        protected.r#use(RejectMiddleware);
        protected.get("/", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("should be rejected"))
        });
        protected.get("/item", |_: crate::domain::Context<()>| async move {
            Ok(Response::text("should be rejected"))
        });

        app.mount("/api/protected", protected);

        // Test 1: Root should work (200 OK)
        let req = crate::domain::Request::new(
            Method::GET,
            "/".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(app.dispatch(req).await.unwrap().status(), StatusCode::OK);

        // Test 2: /public should work (200 OK)
        let req = crate::domain::Request::new(
            Method::GET,
            "/public".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(app.dispatch(req).await.unwrap().status(), StatusCode::OK);

        // Test 3: /api (NOT /api/protected) should work (200 OK)
        let req = crate::domain::Request::new(
            Method::GET,
            "/api".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(
            app.dispatch(req).await.unwrap().status(),
            StatusCode::OK,
            "/api should NOT be affected by /api/protected middleware"
        );

        // Test 4: /api/other (different path) should work (200 OK)
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/other".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(
            app.dispatch(req).await.unwrap().status(),
            StatusCode::OK,
            "/api/other should NOT be affected by /api/protected middleware"
        );

        // Test 5: /api/protected (exact mount) SHOULD be rejected (403 Forbidden)
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/protected".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(
            app.dispatch(req).await.unwrap().status(),
            StatusCode::FORBIDDEN,
            "/api/protected SHOULD be rejected by middleware"
        );

        // Test 6: /api/protected/item SHOULD be rejected (403 Forbidden)
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/protected/item".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(
            app.dispatch(req).await.unwrap().status(),
            StatusCode::FORBIDDEN,
            "/api/protected/item SHOULD be rejected by middleware"
        );

        // Test 7: Similar-looking path /api/protected-but-different should NOT be affected
        let req = crate::domain::Request::new(
            Method::GET,
            "/api/protected-but-different".parse().unwrap(),
            Version::HTTP_11,
            crate::domain::Query::new(),
            crate::http::Headers::new(),
            bytes::Bytes::new(),
        );
        assert_eq!(
            app.dispatch(req).await.unwrap().status(),
            StatusCode::OK,
            "/api/protected-but-different should NOT be affected (different path, not a sub-path)"
        );
    }
}
