#![cfg(feature = "rbac")]

use anyhow::Result;
use axum::{
    Json, Router,
    extract::{Path, State},
    http::StatusCode,
    response::{IntoResponse, Response},
    routing::{get, post},
};
use serde::{Deserialize, Serialize};
use shared::auth::{AccessResult, ResourceType, User};
use std::sync::Arc;
use uuid::Uuid;

use crate::auth::JwtService;
use crate::database::{Database, DatabaseConfig, DatabasePool};
use std::time::Duration;

use crate::auth::{
    rbac_config::RBACConfigLoader, rbac_middleware::*, rbac_repository::RBACRepository,
    rbac_service::RBACService,
};

/// Example application state with RBAC services
#[derive(Clone)]
pub struct AppState {
    pub auth_repository: Arc<server::database::auth::AuthRepository>,
    pub rbac_repository: Arc<RBACRepository>,
    pub rbac_service: Arc<RBACService>,
    pub jwt_service: Arc<JwtService>,
}

/// Example request/response types
#[derive(Serialize, Deserialize)]
pub struct DatabaseQueryRequest {
    pub query: String,
    pub parameters: Vec<String>,
}

#[derive(Serialize, Deserialize)]
pub struct DatabaseQueryResponse {
    pub success: bool,
    pub data: Option<serde_json::Value>,
    pub message: String,
}

#[derive(Serialize, Deserialize)]
pub struct FileAccessRequest {
    pub operation: String, // "read", "write", "delete"
    pub content: Option<String>,
}

#[derive(Serialize, Deserialize)]
pub struct FileAccessResponse {
    pub success: bool,
    pub content: Option<String>,
    pub message: String,
}

#[derive(Serialize, Deserialize)]
pub struct UserCategoryRequest {
    pub category: String,
    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
}

#[derive(Serialize, Deserialize)]
pub struct UserTagRequest {
    pub tag: String,
    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
}

/// Create router with RBAC-protected routes
pub fn create_rbac_routes(state: AppState) -> Router {
    Router::new()
        // Database access routes
        .route("/api/database/:db_name", get(get_database_info))
        .route("/api/database/:db_name/query", post(execute_database_query))
        .route(
            "/api/database/:db_name/admin",
            post(admin_database_operation),
        )
        // File access routes
        .route("/api/files/*path", get(read_file))
        .route("/api/files/*path", post(write_file))
        // Content management routes
        .route("/api/content/:content_id", get(get_content))
        .route("/api/content/:content_id", post(update_content))
        // User management routes
        .route("/api/users/:user_id/categories", post(assign_user_category))
        .route("/api/users/:user_id/tags", post(assign_user_tag))
        .route("/api/users/:user_id/access-check", post(check_user_access))
        // Admin routes
        .route("/api/admin/rbac/config", get(get_rbac_config))
        .route("/api/admin/rbac/config", post(update_rbac_config))
        .route("/api/admin/rbac/audit/:user_id", get(get_access_audit))
        .with_state(state)
}

/// Example: Database access with RBAC
pub async fn get_database_info(
    Path(db_name): Path<String>,
    State(state): State<AppState>,
) -> Result<Response, StatusCode> {
    // The RBAC middleware should have already checked access
    // This is just an example of how to use the context
    Ok(Json(DatabaseQueryResponse {
        success: true,
        data: Some(serde_json::json!({
            "database": db_name,
            "status": "accessible",
            "tables": ["users", "content", "sessions"]
        })),
        message: "Database info retrieved successfully".to_string(),
    })
    .into_response())
}

/// Example: Execute database query with RBAC
pub async fn execute_database_query(
    Path(db_name): Path<String>,
    State(state): State<AppState>,
    Json(request): Json<DatabaseQueryRequest>,
) -> Result<Response, StatusCode> {
    // In a real implementation, you would:
    // 1. Validate the SQL query
    // 2. Check if user has specific table access
    // 3. Execute the query with proper sanitization
    // 4. Return results

    Ok(Json(DatabaseQueryResponse {
        success: true,
        data: Some(serde_json::json!({
            "query": request.query,
            "rows_affected": 42,
            "execution_time": "0.123ms"
        })),
        message: "Query executed successfully".to_string(),
    })
    .into_response())
}

/// Example: Admin database operation
pub async fn admin_database_operation(
    Path(db_name): Path<String>,
    State(state): State<AppState>,
    Json(request): Json<DatabaseQueryRequest>,
) -> Result<Response, StatusCode> {
    // This would be protected by admin-only middleware
    Ok(Json(DatabaseQueryResponse {
        success: true,
        data: Some(serde_json::json!({
            "operation": "admin_operation",
            "database": db_name,
            "status": "completed"
        })),
        message: "Admin operation completed".to_string(),
    })
    .into_response())
}

/// Example: File access with RBAC
pub async fn read_file(
    Path(file_path): Path<String>,
    State(state): State<AppState>,
) -> Result<Response, StatusCode> {
    // File reading logic would go here
    // The RBAC middleware has already checked access

    Ok(Json(FileAccessResponse {
        success: true,
        content: Some("File content here...".to_string()),
        message: "File read successfully".to_string(),
    })
    .into_response())
}

/// Example: Write file with RBAC
pub async fn write_file(
    Path(file_path): Path<String>,
    State(state): State<AppState>,
    Json(request): Json<FileAccessRequest>,
) -> Result<Response, StatusCode> {
    // File writing logic would go here
    // The RBAC middleware has already checked access

    Ok(Json(FileAccessResponse {
        success: true,
        content: None,
        message: "File written successfully".to_string(),
    })
    .into_response())
}

/// Example: Content access with RBAC
pub async fn get_content(
    Path(content_id): Path<String>,
    State(state): State<AppState>,
) -> Result<Response, StatusCode> {
    Ok(Json(serde_json::json!({
        "success": true,
        "content": {
            "id": content_id,
            "title": "Example Content",
            "body": "This is example content...",
            "created_at": chrono::Utc::now()
        }
    }))
    .into_response())
}

/// Example: Update content with RBAC
pub async fn update_content(
    Path(content_id): Path<String>,
    State(state): State<AppState>,
    Json(content): Json<serde_json::Value>,
) -> Result<Response, StatusCode> {
    Ok(Json(serde_json::json!({
        "success": true,
        "message": "Content updated successfully",
        "content_id": content_id
    }))
    .into_response())
}

/// Example: Assign category to user
pub async fn assign_user_category(
    Path(user_id): Path<Uuid>,
    State(state): State<AppState>,
    Json(request): Json<UserCategoryRequest>,
) -> Result<Response, StatusCode> {
    match state
        .rbac_service
        .assign_category_to_user(user_id, &request.category, None, request.expires_at)
        .await
    {
        Ok(_) => Ok(Json(serde_json::json!({
            "success": true,
            "message": "Category assigned successfully"
        }))
        .into_response()),
        Err(_) => Ok(Json(serde_json::json!({
            "success": false,
            "message": "Failed to assign category"
        }))
        .into_response()),
    }
}

/// Example: Assign tag to user
pub async fn assign_user_tag(
    Path(user_id): Path<Uuid>,
    State(state): State<AppState>,
    Json(request): Json<UserTagRequest>,
) -> Result<Response, StatusCode> {
    match state
        .rbac_service
        .assign_tag_to_user(user_id, &request.tag, None, request.expires_at)
        .await
    {
        Ok(_) => Ok(Json(serde_json::json!({
            "success": true,
            "message": "Tag assigned successfully"
        }))
        .into_response()),
        Err(_) => Ok(Json(serde_json::json!({
            "success": false,
            "message": "Failed to assign tag"
        }))
        .into_response()),
    }
}

/// Example: Check user access to a resource
pub async fn check_user_access(
    Path(user_id): Path<Uuid>,
    State(state): State<AppState>,
    Json(request): Json<serde_json::Value>,
) -> Result<Response, StatusCode> {
    // Extract user from database
    let user = match state.auth_repository.find_user_by_id(user_id).await {
        Ok(Some(user)) => user,
        Ok(None) => {
            return Ok(Json(serde_json::json!({
                "success": false,
                "message": "User not found"
            }))
            .into_response());
        }
        Err(_) => {
            return Ok(Json(serde_json::json!({
                "success": false,
                "message": "Database error"
            }))
            .into_response());
        }
    };

    // Check different types of access
    let db_access = state
        .rbac_service
        .check_database_access(
            &user,
            request
                .get("database")
                .and_then(|v| v.as_str())
                .unwrap_or("default"),
            request
                .get("action")
                .and_then(|v| v.as_str())
                .unwrap_or("read"),
        )
        .await
        .unwrap_or(AccessResult::Deny);

    let file_access = state
        .rbac_service
        .check_file_access(
            &user,
            request
                .get("file")
                .and_then(|v| v.as_str())
                .unwrap_or("default"),
            request
                .get("action")
                .and_then(|v| v.as_str())
                .unwrap_or("read"),
        )
        .await
        .unwrap_or(AccessResult::Deny);

    let content_access = state
        .rbac_service
        .check_content_access(
            &user,
            request
                .get("content")
                .and_then(|v| v.as_str())
                .unwrap_or("default"),
            request
                .get("action")
                .and_then(|v| v.as_str())
                .unwrap_or("read"),
        )
        .await
        .unwrap_or(AccessResult::Deny);

    Ok(Json(serde_json::json!({
        "success": true,
        "access_results": {
            "database": format!("{:?}", db_access),
            "file": format!("{:?}", file_access),
            "content": format!("{:?}", content_access)
        },
        "user_categories": user.profile.categories,
        "user_tags": user.profile.tags
    }))
    .into_response())
}

/// Example: Get RBAC configuration
pub async fn get_rbac_config(State(state): State<AppState>) -> Result<Response, StatusCode> {
    match state.rbac_service.get_rbac_config("default").await {
        Ok(config) => Ok(Json(serde_json::json!({
            "success": true,
            "config": config
        }))
        .into_response()),
        Err(_) => Ok(Json(serde_json::json!({
            "success": false,
            "message": "Failed to get RBAC config"
        }))
        .into_response()),
    }
}

/// Example: Update RBAC configuration
pub async fn update_rbac_config(
    State(state): State<AppState>,
    Json(config): Json<shared::auth::RBACConfig>,
) -> Result<Response, StatusCode> {
    match state
        .rbac_service
        .save_rbac_config("default", &config, Some("Updated via API"))
        .await
    {
        Ok(_) => Ok(Json(serde_json::json!({
            "success": true,
            "message": "RBAC config updated successfully"
        }))
        .into_response()),
        Err(_) => Ok(Json(serde_json::json!({
            "success": false,
            "message": "Failed to update RBAC config"
        }))
        .into_response()),
    }
}

/// Example: Get access audit for user
pub async fn get_access_audit(
    Path(user_id): Path<Uuid>,
    State(state): State<AppState>,
) -> Result<Response, StatusCode> {
    match state
        .rbac_service
        .get_user_access_history(user_id, 100)
        .await
    {
        Ok(history) => Ok(Json(serde_json::json!({
            "success": true,
            "audit_log": history
        }))
        .into_response()),
        Err(_) => Ok(Json(serde_json::json!({
            "success": false,
            "message": "Failed to get access audit"
        }))
        .into_response()),
    }
}

/// Example: Setup RBAC middleware for specific routes
pub fn setup_rbac_middleware(app: Router<AppState>) -> Router<AppState> {
    app
        // Database routes with specific access requirements
        .route(
            "/api/database/:db_name",
            get(get_database_info).layer(axum::middleware::from_fn(rbac_middleware)),
        )
        .route(
            "/api/database/:db_name/query",
            post(execute_database_query).layer(axum::middleware::from_fn(rbac_middleware)),
        )
        .route(
            "/api/database/:db_name/admin",
            post(admin_database_operation).layer(axum::middleware::from_fn(
                require_category_access(vec!["admin".to_string()]),
            )),
        )
        // File routes with path-based access
        .route(
            "/api/files/*path",
            get(read_file).layer(axum::middleware::from_fn(rbac_middleware)),
        )
        .route(
            "/api/files/*path",
            post(write_file).layer(axum::middleware::from_fn(rbac_middleware)),
        )
        // Content routes
        .route(
            "/api/content/:content_id",
            get(get_content).layer(axum::middleware::from_fn(rbac_middleware)),
        )
        .route(
            "/api/content/:content_id",
            post(update_content).layer(axum::middleware::from_fn(require_tag_access(vec![
                "editor".to_string(),
            ]))),
        )
        // Admin routes with category requirements
        .route(
            "/api/admin/rbac/config",
            get(get_rbac_config).layer(axum::middleware::from_fn(require_category_access(vec![
                "admin".to_string(),
            ]))),
        )
        .route(
            "/api/admin/rbac/config",
            post(update_rbac_config).layer(axum::middleware::from_fn(require_category_access(
                vec!["admin".to_string()],
            ))),
        )
}

/// Example: Initialize RBAC system
pub async fn initialize_rbac_system(database_url: &str, config_path: &str) -> Result<AppState> {
    // Initialize database connection using new abstraction
    let database_config = DatabaseConfig {
        url: database_url.to_string(),
        max_connections: 10,
        min_connections: 1,
        connect_timeout: Duration::from_secs(30),
        idle_timeout: Duration::from_secs(600),
        max_lifetime: Duration::from_secs(3600),
    };

    let database_pool = DatabasePool::new(&database_config).await?;
    let database = Database::new(database_pool.clone());

    // Initialize repositories using new database abstraction
    let auth_repository = Arc::new(server::database::auth::AuthRepository::new(
        database.create_connection(),
    ));
    let rbac_repository = Arc::new(RBACRepository::from_database_pool(&database_pool));

    // Initialize services
    let jwt_service = Arc::new(
        JwtService::new().map_err(|e| anyhow::anyhow!("Failed to create JWT service: {}", e))?,
    );

    let rbac_service = Arc::new(RBACService::new(rbac_repository.clone()));

    // Load RBAC configuration from TOML
    let config_loader = RBACConfigLoader::new(config_path);
    if !config_loader.config_exists() {
        // Create default config if it doesn't exist
        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("Initial configuration"))
        .await?;

    Ok(AppState {
        rbac_service,
        rbac_repository,
        auth_repository,
        jwt_service,
    })
}

/// Example: Cleanup task for expired permissions
pub async fn cleanup_expired_permissions(state: AppState) -> Result<()> {
    let deleted_count = state.rbac_service.cleanup_expired_cache().await?;
    println!(
        "Cleaned up {} expired permission cache entries",
        deleted_count
    );
    Ok(())
}

/// Example: Sync categories and tags from user profile
pub async fn sync_user_categories_and_tags(
    state: &AppState,
    user_id: Uuid,
    categories: Vec<String>,
    tags: Vec<String>,
) -> Result<()> {
    // Get current user categories and tags
    let current_categories = state.rbac_repository.get_user_categories(user_id).await?;
    let current_tags = state.rbac_repository.get_user_tags(user_id).await?;

    // Add new categories
    for category in &categories {
        if !current_categories.contains(category) {
            state
                .rbac_service
                .assign_category_to_user(user_id, category, None, None)
                .await?;
        }
    }

    // Add new tags
    for tag in &tags {
        if !current_tags.contains(tag) {
            state
                .rbac_service
                .assign_tag_to_user(user_id, tag, None, None)
                .await?;
        }
    }

    // Remove old categories
    for category in &current_categories {
        if !categories.contains(category) {
            state
                .rbac_service
                .remove_category_from_user(user_id, category)
                .await?;
        }
    }

    // Remove old tags
    for tag in &current_tags {
        if !tags.contains(tag) {
            state
                .rbac_service
                .remove_tag_from_user(user_id, tag)
                .await?;
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use axum::body::Body;
    use axum::http::{Method, Request, StatusCode};
    use tower::ServiceExt;

    #[tokio::test]
    async fn test_rbac_integration() {
        // This would require a test database setup
        // For now, just test the structure
        assert!(true);
    }

    #[tokio::test]
    async fn test_config_loading() {
        let loader = RBACConfigLoader::new("test_config.toml");

        // Test default config creation
        let default_config = loader.create_default_rbac_config();
        assert!(!default_config.rules.is_empty());
        assert!(!default_config.default_permissions.is_empty());
    }
}
