use std::sync::Arc;

use anyhow::Result;
use axum::Router;

use crate::config::features::FeatureConfig;
use crate::database::DatabasePool;

/// Conditional RBAC service — feature flag bridge for optional RBAC
/// functionality.
///
/// Database-backed `RBACService`/`RBACRepository` are intentionally excluded
/// from compilation (their modules have unresolved interface issues). This
/// struct retains the `FeatureConfig` gate so call-sites don't change when the
/// DB layer is restored.
#[derive(Clone)]
pub struct ConditionalRBACService {
    pub feature_config: Arc<FeatureConfig>,
}

impl ConditionalRBACService {
    #[allow(dead_code)]
    pub async fn new(
        _database_pool: &DatabasePool,
        feature_config: Arc<FeatureConfig>,
        _rbac_config_path: Option<&str>,
    ) -> Result<Self> {
        Ok(Self { feature_config })
    }

    pub fn is_enabled(&self) -> bool {
        self.feature_config.is_rbac_enabled()
    }

    pub fn is_feature_enabled(&self, feature: &str) -> bool {
        self.feature_config.is_rbac_feature_enabled(feature)
    }

    /// Returns `None` — DB-backed RBAC service is not compiled.
    #[allow(dead_code)]
    pub fn service(&self) -> Option<&Arc<()>> {
        None
    }

    /// Returns `None` — DB-backed RBAC repository is not compiled.
    #[allow(dead_code)]
    pub fn repository(&self) -> Option<&Arc<()>> {
        None
    }

    /// File-based RBAC middleware is applied via `startup::middleware`. This is
    /// a no-op.
    pub fn apply_middleware<S>(&self, router: Router<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        router
    }

    pub fn should_enable_database_access(&self) -> bool {
        self.is_feature_enabled("database_access")
    }

    pub fn should_enable_file_access(&self) -> bool {
        self.is_feature_enabled("file_access")
    }

    #[allow(dead_code)]
    pub fn should_enable_content_access(&self) -> bool {
        self.is_feature_enabled("content_access")
    }

    pub fn should_enable_category_access(&self) -> bool {
        self.is_feature_enabled("category_access")
    }

    #[allow(dead_code)]
    pub fn should_enable_role_based_access(&self) -> bool {
        self.is_feature_enabled("role_based_access")
    }

    /// Check user access. Falls back to role-based check since DB RBAC is not
    /// compiled.
    #[allow(dead_code)]
    pub async fn check_user_access(
        &self,
        user: &rustelo_core_lib::auth::User,
        resource_type: &str,
        resource_name: &str,
        action: &str,
    ) -> Result<rustelo_core_lib::auth::AccessResult> {
        Ok(self.check_basic_access(user, resource_type, resource_name, action))
    }

    fn check_basic_access(
        &self,
        user: &rustelo_core_lib::auth::User,
        _resource_type: &str,
        _resource_name: &str,
        action: &str,
    ) -> rustelo_core_lib::auth::AccessResult {
        use rustelo_core_lib::auth::{AccessResult, Role};

        let has_access = match action {
            "read" => {
                user.has_role(&Role::User)
                    || user.has_role(&Role::Admin)
                    || user.has_role(&Role::Moderator)
            }
            "write" | "create" | "update" => {
                user.has_role(&Role::Admin) || user.has_role(&Role::Moderator)
            }
            "delete" | "admin" => user.has_role(&Role::Admin),
            _ => {
                user.has_role(&Role::User)
                    || user.has_role(&Role::Admin)
                    || user.has_role(&Role::Moderator)
            }
        };

        if has_access {
            AccessResult::Allow
        } else {
            AccessResult::Deny
        }
    }

    #[allow(dead_code)]
    pub async fn start_background_tasks(&self) {
        if !self.is_enabled() {
            return;
        }
        if self.is_feature_enabled("audit_logging") {
            tracing::info!("RBAC audit logging enabled");
        }
    }

    #[allow(dead_code)]
    pub fn get_feature_status(&self) -> serde_json::Value {
        serde_json::json!({
            "rbac_enabled": self.is_enabled(),
            "features": {
                "database_access": self.is_feature_enabled("database_access"),
                "file_access": self.is_feature_enabled("file_access"),
                "content_access": self.is_feature_enabled("content_access"),
                "api_access": self.is_feature_enabled("api_access"),
                "categories": self.is_feature_enabled("categories"),
                "tags": self.is_feature_enabled("tags"),
                "caching": self.is_feature_enabled("caching"),
                "audit_logging": self.is_feature_enabled("audit_logging"),
                "toml_config": self.is_feature_enabled("toml_config"),
                "hierarchical_permissions": self.is_feature_enabled("hierarchical_permissions"),
                "dynamic_rules": self.is_feature_enabled("dynamic_rules")
            }
        })
    }

    #[allow(dead_code)]
    pub fn create_rbac_routes<S>(&self) -> Option<Router<S>>
    where
        S: Clone + Send + Sync + 'static,
    {
        if !self.is_enabled() {
            return None;
        }

        use axum::{routing::get, Json};
        use serde_json::json;

        let mut router = Router::new();

        router = router.route(
            "/api/rbac/status",
            get(|| async { Json(json!({ "enabled": true, "message": "RBAC system is active" })) }),
        );

        if self.is_feature_enabled("categories") {
            router = router.route(
                "/api/rbac/categories",
                get(|| async {
                    Json(json!({
                        "categories": ["admin", "editor", "viewer", "finance", "hr", "it"]
                    }))
                }),
            );
        }

        if self.is_feature_enabled("tags") {
            router = router.route(
                "/api/rbac/tags",
                get(|| async {
                    Json(json!({
                        "tags": ["sensitive", "public", "internal", "confidential", "restricted"]
                    }))
                }),
            );
        }

        if self.is_feature_enabled("audit_logging") {
            router = router.route(
                "/api/rbac/audit/:user_id",
                get(|| async { Json(json!({ "message": "Audit log endpoint" })) }),
            );
        }

        Some(router)
    }
}

/// Extension trait for conditional RBAC middleware application on `Router`.
pub trait ConditionalRBACExt<S> {
    #[allow(dead_code)]
    fn apply_rbac_if_enabled(self, rbac: &ConditionalRBACService) -> Self;

    #[allow(dead_code)]
    fn require_database_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        database_name: String,
        action: String,
    ) -> Self;

    #[allow(dead_code)]
    fn require_file_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        file_path: String,
        action: String,
    ) -> Self;

    #[allow(dead_code)]
    fn require_category_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        categories: Vec<String>,
    ) -> Self;
}

impl<S> ConditionalRBACExt<S> for Router<S>
where
    S: Clone + Send + Sync + 'static,
{
    fn apply_rbac_if_enabled(self, rbac: &ConditionalRBACService) -> Self {
        rbac.apply_middleware(self)
    }

    fn require_database_access_if_enabled(
        self,
        _rbac: &ConditionalRBACService,
        _database_name: String,
        _action: String,
    ) -> Self {
        self
    }

    fn require_file_access_if_enabled(
        self,
        _rbac: &ConditionalRBACService,
        _file_path: String,
        _action: String,
    ) -> Self {
        self
    }

    fn require_category_access_if_enabled(
        self,
        _rbac: &ConditionalRBACService,
        _categories: Vec<String>,
    ) -> Self {
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::features::FeatureConfig;

    #[tokio::test]
    async fn test_conditional_rbac_disabled() {
        let feature_config = Arc::new(FeatureConfig::default());

        let pool = match sqlx::PgPool::connect("postgres://test").await {
            Ok(pool) => pool,
            Err(_) => return, // skip if no test DB
        };

        let database_pool = DatabasePool::PostgreSQL(pool);
        let rbac = ConditionalRBACService::new(&database_pool, feature_config, None)
            .await
            .unwrap();

        assert!(!rbac.is_enabled());
        assert!(rbac.service().is_none());
    }

    #[tokio::test]
    async fn test_conditional_rbac_enabled() {
        let mut feature_config = FeatureConfig::default();
        feature_config.enable_rbac();
        let feature_config = Arc::new(feature_config);

        assert!(feature_config.is_rbac_enabled());
        assert!(feature_config.is_rbac_feature_enabled("database_access"));
    }

    #[test]
    fn test_basic_access_control() {
        use rustelo_core_lib::auth::{Role, User, UserProfile};

        let feature_config = Arc::new(FeatureConfig::default());
        let rbac = ConditionalRBACService { feature_config };

        let admin_user = User {
            id: uuid::Uuid::new_v4(),
            email: "admin@example.com".to_string(),
            username: "admin".to_string(),
            display_name: None,
            avatar_url: None,
            roles: vec![Role::Admin],
            is_active: true,
            email_verified: true,
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            last_login: None,
            two_factor_enabled: false,
            profile: UserProfile::default(),
        };

        let result = rbac.check_basic_access(&admin_user, "database", "test_db", "read");
        assert_eq!(result, rustelo_core_lib::auth::AccessResult::Allow);

        let result = rbac.check_basic_access(&admin_user, "database", "test_db", "delete");
        assert_eq!(result, rustelo_core_lib::auth::AccessResult::Allow);
    }
}
