use crate::database::DatabasePool;
use anyhow::Result;
use axum::Router;
use std::sync::Arc;

#[cfg(feature = "rbac")]
use super::{
    rbac_config::RBACConfigLoader, rbac_middleware::rbac_middleware,
    rbac_repository::RBACRepository, rbac_service::RBACService,
};
use crate::config::features::FeatureConfig;

/// Conditional RBAC service that handles optional RBAC functionality
#[derive(Clone)]
pub struct ConditionalRBACService {
    #[cfg(feature = "rbac")]
    pub rbac_service: Option<Arc<RBACService>>,
    #[cfg(feature = "rbac")]
    pub rbac_repository: Option<Arc<RBACRepository>>,
    pub feature_config: Arc<FeatureConfig>,
}

impl ConditionalRBACService {
    /// Initialize RBAC service based on feature configuration
    #[allow(dead_code)]
    pub async fn new(
        database_pool: &DatabasePool,
        feature_config: Arc<FeatureConfig>,
        rbac_config_path: Option<&str>,
    ) -> Result<Self> {
        #[cfg(feature = "rbac")]
        let (rbac_service, rbac_repository) = if feature_config.is_rbac_enabled() {
            println!("🔐 Initializing RBAC system...");

            // Initialize RBAC repository
            let rbac_repository = Arc::new(RBACRepository::from_database_pool(database_pool));

            // Initialize RBAC service
            let rbac_service = Arc::new(RBACService::new(rbac_repository.clone()));

            // Load configuration if TOML config is enabled
            if feature_config.is_rbac_feature_enabled("toml_config") {
                if let Some(config_path) = rbac_config_path {
                    let config_loader = RBACConfigLoader::new(config_path);

                    // Create default config if it doesn't exist
                    if !config_loader.config_exists() {
                        println!("📝 Creating default RBAC configuration...");
                        config_loader.create_default_config().await?;
                    }

                    // Load and save config to database
                    let rbac_config = config_loader.load_from_file().await?;
                    rbac_service
                        .save_rbac_config("default", &rbac_config, Some("Feature initialization"))
                        .await?;

                    println!(
                        "✅ RBAC configuration loaded with {} rules",
                        rbac_config.rules.len()
                    );
                } else {
                    println!("⚠️  RBAC TOML config enabled but no config path provided");
                }
            }

            println!("✅ RBAC system initialized successfully");
            (Some(rbac_service), Some(rbac_repository))
        } else {
            println!("ℹ️  RBAC system disabled - using basic role-based authentication");
            (None, None)
        };

        #[cfg(not(feature = "rbac"))]
        let (rbac_service, rbac_repository): (Option<Arc<()>>, Option<Arc<()>>) = {
            println!("ℹ️  RBAC system disabled - feature not compiled");
            (None, None)
        };

        #[cfg(feature = "rbac")]
        let result = Self {
            rbac_service,
            rbac_repository,
            feature_config,
        };

        #[cfg(not(feature = "rbac"))]
        let result = Self { feature_config };

        Ok(result)
    }

    /// Check if RBAC is enabled
    pub fn is_enabled(&self) -> bool {
        self.feature_config.is_rbac_enabled()
    }

    /// Check if a specific RBAC feature is enabled
    pub fn is_feature_enabled(&self, feature: &str) -> bool {
        self.feature_config.is_rbac_feature_enabled(feature)
    }

    /// Get RBAC service (if enabled)
    /// Get RBAC service (if enabled) - no-op when rbac feature is disabled
    #[cfg(feature = "rbac")]
    #[allow(dead_code)]
    pub fn service(&self) -> Option<&Arc<RBACService>> {
        self.rbac_service.as_ref()
    }

    /// Get RBAC repository (if enabled) - no-op when rbac feature is disabled
    #[cfg(feature = "rbac")]
    #[allow(dead_code)]
    pub fn repository(&self) -> Option<&Arc<RBACRepository>> {
        self.rbac_repository.as_ref()
    }

    /// Get RBAC service (if enabled) - no-op when rbac feature is disabled
    #[cfg(not(feature = "rbac"))]
    #[allow(dead_code)]
    pub fn service(&self) -> Option<&Arc<()>> {
        None
    }

    /// Get RBAC repository (if enabled) - no-op when rbac feature is disabled
    #[cfg(not(feature = "rbac"))]
    #[allow(dead_code)]
    pub fn repository(&self) -> Option<&Arc<()>> {
        None
    }

    /// Apply RBAC middleware conditionally
    pub fn apply_middleware<S>(&self, router: Router<S>) -> Router<S>
    where
        S: Clone + Send + Sync + 'static,
    {
        if self.is_enabled() {
            #[cfg(feature = "rbac")]
            if let Some(rbac_service) = &self.rbac_service {
                println!("🛡️  Applying RBAC middleware");
                return router.layer(middleware::from_fn_with_state(
                    rbac_service.clone(),
                    rbac_middleware,
                ));
            }
            #[cfg(not(feature = "rbac"))]
            {
                println!("⚠️  RBAC enabled but service not available (rbac feature disabled)");
            }
        }

        println!("ℹ️  Skipping RBAC middleware (disabled)");
        router
    }

    /// Check if database access middleware should be enabled
    pub fn should_enable_database_access(&self) -> bool {
        self.is_feature_enabled("database_access")
    }

    /// Check if file access middleware should be enabled
    pub fn should_enable_file_access(&self) -> bool {
        self.is_feature_enabled("file_access")
    }

    /// Check if content access should be enabled
    #[allow(dead_code)]
    pub fn should_enable_content_access(&self) -> bool {
        self.is_feature_enabled("content_access")
    }

    /// Check if category access middleware should be enabled
    pub fn should_enable_category_access(&self) -> bool {
        self.is_feature_enabled("category_access")
    }

    /// Check if role-based access should be enabled
    #[allow(dead_code)]
    pub fn should_enable_role_based_access(&self) -> bool {
        self.is_feature_enabled("role_based_access")
    }

    /// Check user access with fallback to basic role check
    #[allow(dead_code)]
    pub async fn check_user_access(
        &self,
        user: &shared::auth::User,
        resource_type: &str,
        resource_name: &str,
        action: &str,
    ) -> Result<shared::auth::AccessResult> {
        #[cfg(feature = "rbac")]
        if let Some(rbac_service) = &self.rbac_service {
            // Use RBAC system
            return match resource_type {
                "database" => {
                    rbac_service
                        .check_database_access(user, resource_name, action)
                        .await
                }
                "file" => {
                    rbac_service
                        .check_file_access(user, resource_name, action)
                        .await
                }
                "content" => {
                    rbac_service
                        .check_content_access(user, resource_name, action)
                        .await
                }
                _ => {
                    // Use custom access context
                    let context = shared::auth::AccessContext {
                        user: Some(user.clone()),
                        resource_type: match resource_type {
                            "database" => shared::auth::ResourceType::Database,
                            "file" => shared::auth::ResourceType::File,
                            "content" => shared::auth::ResourceType::Content,
                            "api" => shared::auth::ResourceType::Api,
                            "directory" => shared::auth::ResourceType::Directory,
                            _ => shared::auth::ResourceType::Custom(resource_type.to_string()),
                        },
                        resource_name: resource_name.to_string(),
                        action: action.to_string(),
                        additional_context: std::collections::HashMap::new(),
                    };
                    rbac_service.check_access(&context).await
                }
            };
        }

        // Fallback to basic role-based access control
        Ok(self.check_basic_access(user, resource_type, resource_name, action))
    }

    /// Basic access control fallback when RBAC is disabled
    #[allow(dead_code)]
    fn check_basic_access(
        &self,
        user: &shared::auth::User,
        resource_type: &str,
        _resource_name: &str,
        action: &str,
    ) -> shared::auth::AccessResult {
        use shared::auth::{AccessResult, Role};

        // Simple role-based checks
        let has_access = match action {
            "read" => {
                // Users can read most resources
                user.has_role(&Role::User)
                    || user.has_role(&Role::Admin)
                    || user.has_role(&Role::Moderator)
            }
            "write" | "create" | "update" => {
                // Only moderators and admins can write
                user.has_role(&Role::Admin) || user.has_role(&Role::Moderator)
            }
            "delete" => {
                // Only admins can delete
                user.has_role(&Role::Admin)
            }
            "admin" => {
                // Only admins for admin operations
                user.has_role(&Role::Admin)
            }
            _ => {
                // Default to requiring at least user role
                user.has_role(&Role::User)
                    || user.has_role(&Role::Admin)
                    || user.has_role(&Role::Moderator)
            }
        };

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

    /// Start background tasks if RBAC features require them
    #[allow(dead_code)]
    pub async fn start_background_tasks(&self) {
        if !self.is_enabled() {
            return;
        }

        if self.is_feature_enabled("caching") {
            #[cfg(feature = "rbac")]
            if let Some(rbac_service) = &self.rbac_service {
                let _service = rbac_service.clone();
                tokio::spawn(async move {
                    let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(300)); // 5 minutes
                    loop {
                        interval.tick().await;
                        if let Err(e) = service.cleanup_expired_cache().await {
                            eprintln!("🧹 Error cleaning up RBAC cache: {}", e);
                        } else {
                            println!("🧹 RBAC cache cleanup completed");
                        }
                    }
                });
                println!("🚀 Started RBAC cache cleanup background task");
            }
        }

        if self.is_feature_enabled("audit_logging") {
            // Could add audit log rotation or analysis tasks here
            println!("🚀 RBAC audit logging is enabled");
        }
    }

    /// Get feature status for API responses
    #[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")
            }
        })
    }

    /// Create RBAC routes conditionally
    #[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::{
            Json,
            routing::{get, post},
        };
        use serde_json::json;

        let mut router = Router::new();

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

        // Add category management routes if enabled
        if self.is_feature_enabled("categories") {
            router = router
                .route(
                    "/api/rbac/categories",
                    get(|| async {
                        Json(json!({
                            "categories": ["admin", "editor", "viewer", "finance", "hr", "it"]
                        }))
                    }),
                )
                .route(
                    "/api/rbac/categories",
                    post(|| async {
                        Json(json!({
                            "message": "Category management endpoint"
                        }))
                    }),
                );
        }

        // Add tag management routes if enabled
        if self.is_feature_enabled("tags") {
            router = router
                .route("/api/rbac/tags", get(|| async {
                    Json(json!({
                        "tags": ["sensitive", "public", "internal", "confidential", "restricted"]
                    }))
                }))
                .route("/api/rbac/tags", post(|| async {
                    Json(json!({
                        "message": "Tag management endpoint"
                    }))
                }));
        }

        // Add audit routes if enabled
        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)
    }
}

/// Helper macro for conditional RBAC middleware application
#[macro_export]
macro_rules! apply_rbac_middleware {
    ($router:expr, $rbac:expr, $middleware_fn:expr) => {
        if $rbac.is_enabled() {
            if let Some(middleware) = $middleware_fn {
                $router.layer(axum::middleware::from_fn(middleware))
            } else {
                $router
            }
        } else {
            $router
        }
    };
}

/// Helper trait for conditional RBAC application
pub trait ConditionalRBACExt<S> {
    /// Apply RBAC middleware if enabled
    #[allow(dead_code)]
    fn apply_rbac_if_enabled(self, rbac: &ConditionalRBACService) -> Self;

    /// Apply database access middleware if enabled
    #[allow(dead_code)]
    fn require_database_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        database_name: String,
        action: String,
    ) -> Self;

    /// Apply file access middleware if enabled
    #[allow(dead_code)]
    fn require_file_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        file_path: String,
        action: String,
    ) -> Self;

    /// Apply category access middleware if enabled
    #[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,
{
    #[allow(dead_code)]
    fn apply_rbac_if_enabled(self, rbac: &ConditionalRBACService) -> Self {
        rbac.apply_middleware(self)
    }

    #[allow(dead_code)]
    fn require_database_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        database_name: String,
        action: String,
    ) -> Self {
        if rbac.should_enable_database_access() {
            #[cfg(feature = "rbac")]
            {
                self.layer(middleware::from_fn_with_state(
                    (database_name, action),
                    super::rbac_middleware::require_database_access,
                ))
            }
            #[cfg(not(feature = "rbac"))]
            {
                self
            }
        } else {
            self
        }
    }

    #[allow(dead_code)]
    fn require_file_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        file_path: String,
        action: String,
    ) -> Self {
        if rbac.should_enable_file_access() {
            #[cfg(feature = "rbac")]
            {
                self.layer(middleware::from_fn_with_state(
                    (file_path, action),
                    super::rbac_middleware::require_file_access,
                ))
            }
            #[cfg(not(feature = "rbac"))]
            {
                self
            }
        } else {
            self
        }
    }

    #[allow(dead_code)]
    fn require_category_access_if_enabled(
        self,
        rbac: &ConditionalRBACService,
        categories: Vec<String>,
    ) -> Self {
        if rbac.should_enable_category_access() {
            #[cfg(feature = "rbac")]
            {
                self.layer(middleware::from_fn_with_state(
                    categories,
                    super::rbac_middleware::require_category_access,
                ))
            }
            #[cfg(not(feature = "rbac"))]
            {
                self
            }
        } else {
            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()); // RBAC disabled by default

        // Use a dummy connection string for testing
        let pool = match sqlx::PgPool::connect("postgres://test").await {
            Ok(pool) => pool,
            Err(_) => {
                // Skip test if no test database
                return;
            }
        };

        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);

        // This would require a test database setup
        // For now, just test the configuration logic
        assert!(feature_config.is_rbac_enabled());
        assert!(feature_config.is_rbac_feature_enabled("database_access"));
    }

    #[test]
    fn test_basic_access_control() {
        use shared::auth::{Role, User, UserProfile};
        // use std::collections::HashMap;

        let feature_config = Arc::new(FeatureConfig::default());
        let rbac = ConditionalRBACService {
            #[cfg(feature = "rbac")]
            rbac_service: None,
            #[cfg(feature = "rbac")]
            rbac_repository: None,
            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, shared::auth::AccessResult::Allow);

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