//! Axum router composition helpers for Athena Auth server startup.
//!
//! This module centralizes base-path-aware auth route composition so `server.rs`
//! can focus on runtime bootstrapping. It also makes Axum 0.8 wildcard syntax
//! explicit and keeps legacy v0.7 path-check handling policy in one place.

use axum::Router;
use axum::routing::MethodRouter;

/// Axum path-check policy controlling whether v0.7 route syntax checks stay enabled.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum V07PathCheckPolicy {
    /// Enforce Axum 0.8 path syntax checks (`/{segment}` and `/{*wildcard}`).
    #[default]
    Enforce,
    /// Disable v0.7 compatibility checks via `Router::without_v07_checks`.
    Disable,
}

/// Contract for composing auth routers that are mounted at a configurable base path.
pub trait RouterComposer {
    /// Returns the normalized auth base path used for mounting.
    fn base_path(&self) -> &str;

    /// Builds the auth dispatch router before it is merged or nested.
    fn build_auth_router<S>(&self, dispatch_handler: MethodRouter<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static;

    /// Mounts the auth router onto the base router using merge-or-nest semantics.
    fn mount_auth_router<S>(&self, base_router: Router<S>, auth_router: Router<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static;
}

/// Default Axum router composer for Athena Auth base-path composition.
#[derive(Debug, Clone)]
pub struct AxumRouterComposer {
    base_path: String,
    v07_path_check_policy: V07PathCheckPolicy,
}

impl AxumRouterComposer {
    /// Canonical Axum 0.8 wildcard capture route used for catch-all auth dispatch.
    pub const AXUM08_WILDCARD_ROUTE: &str = "/{*path}";

    /// Creates a new composer for the provided normalized base path.
    pub fn new(base_path: impl Into<String>) -> Self {
        Self {
            base_path: base_path.into(),
            v07_path_check_policy: V07PathCheckPolicy::default(),
        }
    }

    /// Sets the v0.7 path-check compatibility policy.
    pub fn with_v07_path_check_policy(mut self, policy: V07PathCheckPolicy) -> Self {
        self.v07_path_check_policy = policy;
        self
    }

    /// Returns true when auth routes should mount at the server root.
    pub fn is_root_mount(&self) -> bool {
        self.base_path == "/"
    }

    /// Applies configured v0.7 check behavior before route merge/nest operations.
    fn apply_pre_merge_v07_policy<S>(&self, router: Router<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        match self.v07_path_check_policy {
            V07PathCheckPolicy::Enforce => router,
            V07PathCheckPolicy::Disable => router.without_v07_checks(),
        }
    }

    /// Applies configured v0.7 check behavior after merge/nest composition.
    fn apply_post_merge_v07_policy<S>(&self, router: Router<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        match self.v07_path_check_policy {
            V07PathCheckPolicy::Enforce => router,
            V07PathCheckPolicy::Disable => router.without_v07_checks(),
        }
    }
}

impl RouterComposer for AxumRouterComposer {
    fn base_path(&self) -> &str {
        &self.base_path
    }

    fn build_auth_router<S>(&self, dispatch_handler: MethodRouter<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        tracing::trace!(
            base_path = %self.base_path,
            root_mount = self.is_root_mount(),
            "building auth router"
        );
        let auth_router: Router<S> = if self.is_root_mount() {
            Router::new().route(Self::AXUM08_WILDCARD_ROUTE, dispatch_handler)
        } else {
            Router::new()
                .route("/", dispatch_handler.clone())
                .route(Self::AXUM08_WILDCARD_ROUTE, dispatch_handler)
        };

        self.apply_pre_merge_v07_policy(auth_router)
    }

    fn mount_auth_router<S>(&self, base_router: Router<S>, auth_router: Router<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        tracing::trace!(
            base_path = %self.base_path,
            root_mount = self.is_root_mount(),
            "mounting auth router"
        );
        let composed_router: Router<S> = if self.is_root_mount() {
            base_router.merge(auth_router)
        } else {
            base_router.nest(self.base_path(), auth_router)
        };

        self.apply_post_merge_v07_policy(composed_router)
    }
}
