//! Example demonstrating how to integrate encryption features into a main server setup
//!
//! This example shows how to:
//! - Initialize the crypto system with encrypted config and sessions
//! - Set up middleware for encrypted sessions
//! - Create protected routes using encrypted user data
//! - Handle encrypted configuration for database connections
//! - Implement secure login/logout with encrypted sessions

// Temporarily disable this example due to API compatibility issues
#![allow(dead_code)]
#![cfg(feature = "crypto-integration-example")]

use axum::{
    Router,
    extract::{Request, State},
    http::StatusCode,
    middleware::{self, Next},
    response::Json,
    routing::{get, post},
};
use chrono::Utc;
use serde::{Deserialize, Serialize};
use rustelo_server::crypto::{
    CryptoService,
    config::{EncryptedConfigBuilder, EncryptedConfigStore},
    integration::AppStateWithCrypto,
    session::{
        EncryptedSessionConfig, EncryptedSessionStore, encrypted_session_middleware,
        session_helpers,
    },
};
use rustelo_core_lib::auth::{LoginCredentials, Role, User, UserProfile};
use std::sync::Arc;
use tower_cookies::{CookieManagerLayer, Cookies};
use tracing::{error, info, warn};
use uuid::Uuid;

/// Main server state with crypto integration
#[derive(Clone)]
pub struct ServerState {
    pub crypto: Arc<CryptoService>,
    pub session_store: Arc<EncryptedSessionStore>,
    pub config_store: Arc<EncryptedConfigStore>,
}

impl ServerState {
    /// Initialize the server state with full crypto integration
    pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
        info!("Initializing crypto-enabled server...");

        // 1. Initialize crypto service
        let crypto = Arc::new(CryptoService::new()?);
        info!("✓ Crypto service initialized");

        // 2. Initialize encrypted session store
        let session_config = EncryptedSessionConfig {
            cookie_name: "rustelo_secure_session".to_string(),
            session_lifetime: match std::env::var("SESSION_LIFETIME_HOURS") {
                Ok(hours) => hours.parse::<i64>().unwrap_or(24) * 3600,
                Err(_) => 24 * 3600, // Default 24 hours
            },
            secure: std::env::var("ENVIRONMENT").unwrap_or_default() == "production",
            http_only: true,
            path: "/".to_string(),
            domain: std::env::var("COOKIE_DOMAIN").ok(),
            same_site: tower_sessions::cookie::SameSite::Lax,
        };

        let session_store = Arc::new(EncryptedSessionStore::new(crypto.clone(), session_config));
        info!("✓ Encrypted session store initialized");

        // 3. Initialize encrypted config store
        let config_file = std::env::var("ENCRYPTED_CONFIG_FILE")
            .unwrap_or_else(|_| "config/encrypted.json".to_string());

        let config_store = Arc::new(
            EncryptedConfigBuilder::new(crypto.clone())
                .with_file(config_file.clone())
                .with_auto_load_env()
                .build()
                .await?,
        );
        info!("✓ Encrypted config store initialized from: {}", config_file);

        // 4. Log configuration status
        let encryption_status = config_store.get_encryption_status();
        let encrypted_count = encryption_status.values().filter(|&&v| v).count();
        let total_count = encryption_status.len();
        info!(
            "✓ Config loaded: {} total keys, {} encrypted",
            total_count, encrypted_count
        );

        Ok(Self {
            crypto,
            session_store,
            config_store,
        })
    }

    /// Get database URL from encrypted config
    pub fn get_database_url(&self) -> Result<String, Box<dyn std::error::Error>> {
        Ok(rustelo_server::crypto::config::rustelo_utils::get_database_url(
            &self.config_store,
        )?)
    }

    /// Get JWT secret from encrypted config
    pub fn get_jwt_secret(&self) -> Result<String, Box<dyn std::error::Error>> {
        Ok(rustelo_server::crypto::config::rustelo_utils::get_jwt_secret(
            &self.config_store,
        )?)
    }

    /// Get any encrypted config value
    pub fn get_config(&self, key: &str) -> Result<Option<String>, Box<dyn std::error::Error>> {
        Ok(self.config_store.get(key)?)
    }
}

/// Login request with encrypted session response
#[derive(Debug, Deserialize)]
pub struct LoginRequest {
    pub credentials: LoginCredentials,
}

/// Login response with session info
#[derive(Debug, Serialize)]
pub struct LoginResponse {
    pub success: bool,
    pub user: User,
    pub session_expires_in: i64,
    pub message: String,
}

/// User profile update request
#[derive(Debug, Deserialize)]
pub struct UpdateProfileRequest {
    pub display_name: Option<String>,
    pub categories: Option<Vec<String>>,
    pub tags: Option<Vec<String>>,
    pub preferences: Option<std::collections::HashMap<String, String>>,
}

/// Protected route handler that requires authentication
pub async fn protected_dashboard(
    request: Request,
    State(state): State<ServerState>,
) -> Result<Json<serde_json::Value>, StatusCode> {
    // Get session data from encrypted session
    if let Some(encrypted_session) = state
        .session_store
        .get_session_cookie(&get_cookies_from_request(&request))
    {
        match state.session_store.get_session(&encrypted_session) {
            Ok(session_data) => {
                // User is authenticated, return dashboard data
                Ok(Json(serde_json::json!({
                    "message": "Welcome to your secure dashboard",
                    "user": {
                        "id": session_data.user_id,
                        "email": session_data.email,
                        "username": session_data.username,
                        "display_name": session_data.display_name,
                        "roles": session_data.roles,
                        "categories": session_data.categories,
                        "tags": session_data.tags,
                        "preferences": session_data.preferences,
                    },
                    "session_info": {
                        "created_at": session_data.created_at,
                        "expires_at": session_data.expires_at,
                        "remaining_seconds": session_data.expires_at - chrono::Utc::now().timestamp(),
                    }
                })))
            }
            Err(e) => {
                warn!("Failed to decrypt session: {}", e);
                // Remove invalid session
                state
                    .session_store
                    .remove_session_cookie(&get_cookies_from_request(&request));
                Err(StatusCode::UNAUTHORIZED)
            }
        }
    } else {
        Err(StatusCode::UNAUTHORIZED)
    }
}

/// Login handler with encrypted session creation
pub async fn login_handler(
    State(state): State<ServerState>,
    cookies: Cookies,
    Json(login_req): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, StatusCode> {
    info!("Login attempt for user: {}", login_req.credentials.email);

    // In a real application, you would validate credentials against a database
    // For this example, we'll create a mock user
    let user = create_mock_user_from_credentials(&login_req.credentials);

    // Create encrypted session
    match session_helpers::login_user(&state.session_store, &cookies, &user).await {
        Ok(_encrypted_session) => {
            info!(
                "✓ User {} logged in successfully with encrypted session",
                user.email
            );

            Ok(Json(LoginResponse {
                success: true,
                user,
                session_expires_in: state.session_store.config.session_lifetime,
                message: "Login successful".to_string(),
            }))
        }
        Err(e) => {
            error!(
                "Failed to create encrypted session for {}: {}",
                user.email, e
            );
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }
    }
}

/// Logout handler with encrypted session cleanup
pub async fn logout_handler(
    State(state): State<ServerState>,
    cookies: Cookies,
) -> Result<Json<serde_json::Value>, StatusCode> {
    session_helpers::logout_user(&state.session_store, &cookies);
    info!("User logged out, encrypted session cleared");

    Ok(Json(serde_json::json!({
        "success": true,
        "message": "Logout successful"
    })))
}

/// Get current user from encrypted session
pub async fn get_current_user(
    State(state): State<ServerState>,
    cookies: Cookies,
) -> Result<Json<User>, StatusCode> {
    if let Some(encrypted_session) = state.session_store.get_session_cookie(&cookies) {
        match state.session_store.get_session(&encrypted_session) {
            Ok(session_data) => {
                let user = session_data_to_user(&session_data);
                Ok(Json(user))
            }
            Err(e) => {
                warn!("Failed to decrypt session: {}", e);
                state.session_store.remove_session_cookie(&cookies);
                Err(StatusCode::UNAUTHORIZED)
            }
        }
    } else {
        Err(StatusCode::UNAUTHORIZED)
    }
}

/// Update user profile with encrypted session refresh
pub async fn update_profile(
    State(state): State<ServerState>,
    cookies: Cookies,
    Json(update_req): Json<UpdateProfileRequest>,
) -> Result<Json<User>, StatusCode> {
    if let Some(encrypted_session) = state.session_store.get_session_cookie(&cookies) {
        match state.session_store.get_session(&encrypted_session) {
            Ok(session_data) => {
                let mut user = session_data_to_user(&session_data);

                // Update profile fields
                if let Some(display_name) = update_req.display_name {
                    user.display_name = Some(display_name);
                }
                if let Some(categories) = update_req.categories {
                    user.profile.categories = categories;
                }
                if let Some(tags) = update_req.tags {
                    user.profile.tags = tags;
                }
                if let Some(preferences) = update_req.preferences {
                    user.profile.preferences = preferences;
                }

                // Update timestamp
                user.updated_at = Utc::now();

                // Create new encrypted session with updated user data
                match session_helpers::refresh_user_session(&state.session_store, &cookies, &user)
                    .await
                {
                    Ok(_) => {
                        info!("✓ User profile updated with encrypted session refresh");
                        Ok(Json(user))
                    }
                    Err(e) => {
                        error!("Failed to refresh encrypted session: {}", e);
                        Err(StatusCode::INTERNAL_SERVER_ERROR)
                    }
                }
            }
            Err(e) => {
                warn!("Failed to decrypt session: {}", e);
                state.session_store.remove_session_cookie(&cookies);
                Err(StatusCode::UNAUTHORIZED)
            }
        }
    } else {
        Err(StatusCode::UNAUTHORIZED)
    }
}

/// Admin endpoint to view system status (requires admin role)
pub async fn admin_system_status(
    State(state): State<ServerState>,
    cookies: Cookies,
) -> Result<Json<serde_json::Value>, StatusCode> {
    // Check if user is admin
    if !is_admin_user(&state, &cookies).await {
        return Err(StatusCode::FORBIDDEN);
    }

    let config_status = state.config_store.get_encryption_status();
    let config_keys = state.config_store.keys();

    Ok(Json(serde_json::json!({
        "system_status": "healthy",
        "crypto_key_loaded": true,
        "session_config": {
            "cookie_name": state.session_store.config.cookie_name,
            "session_lifetime": state.session_store.config.session_lifetime,
            "secure": state.session_store.config.secure,
            "http_only": state.session_store.config.http_only,
        },
        "config_store": {
            "total_keys": config_keys.len(),
            "encrypted_keys": config_status.values().filter(|&&v| v).count(),
            "plain_keys": config_status.values().filter(|&&v| !v).count(),
            "keys": config_keys,
        }
    })))
}

/// Create the main application router with crypto integration
pub fn create_app(state: ServerState) -> Router {
    Router::new()
        // Public routes
        .route("/", get(|| async { "Rustelo Crypto Demo Server" }))
        .route("/api/login", post(login_handler))
        .route("/api/logout", post(logout_handler))
        // Protected routes (require authentication)
        .route("/api/me", get(get_current_user))
        .route("/api/profile", post(update_profile))
        .route("/api/dashboard", get(protected_dashboard))
        // Admin routes (require admin role)
        .route("/api/admin/status", get(admin_system_status))
        // Add state
        .with_state(state)
        // Add cookie middleware
        .layer(CookieManagerLayer::new())
        // Add custom encrypted session middleware
        .layer(middleware::from_fn_with_state(
            // Note: In practice, you'd pass the actual session store here
            Arc::new(EncryptedSessionStore::with_default_config(Arc::new(
                CryptoService::new().unwrap(),
            ))),
            encrypted_session_middleware,
        ))
}

/// Example main function showing how to start the server
pub async fn run_example_server() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize logging
    tracing_subscriber::fmt::init();

    // Initialize server state with crypto
    let state = ServerState::new().await?;

    // Log database connection info (without exposing secrets)
    match state.get_database_url() {
        Ok(db_url) => {
            let masked_url = rustelo_server::crypto::rustelo_utils::mask_sensitive_data(&db_url);
            info!("Database configured: {}", masked_url);
        }
        Err(e) => {
            warn!("Database URL not available: {}", e);
        }
    }

    // Create application
    let app = create_app(state);

    // Start server
    let host = std::env::var("SERVER_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
    let port = std::env::var("SERVER_PORT").unwrap_or_else(|_| "3030".to_string());
    let addr = format!("{}:{}", host, port);

    info!("🚀 Starting secure server on {}", addr);
    info!("🔐 Encryption enabled for sessions and config");
    info!("📡 Ready to handle encrypted requests");

    let listener = tokio::net::TcpListener::bind(&addr).await?;
    axum::serve(listener, app).await?;

    Ok(())
}

/// Helper function to create a mock user from credentials
fn create_mock_user_from_credentials(credentials: &LoginCredentials) -> User {
    // In a real app, you'd validate credentials and fetch user from database
    User {
        id: Uuid::new_v4(),
        email: credentials.email.clone(),
        username: credentials
            .email
            .split('@')
            .next()
            .unwrap_or("user")
            .to_string(),
        display_name: Some(format!(
            "User {}",
            credentials.email.split('@').next().unwrap_or("Unknown")
        )),
        avatar_url: None,
        roles: vec![Role::User], // Could be Admin for testing admin features
        is_active: true,
        email_verified: true,
        created_at: Utc::now(),
        updated_at: Utc::now(),
        last_login: Some(Utc::now()),
        two_factor_enabled: false,
        profile: UserProfile {
            first_name: None,
            last_name: None,
            bio: None,
            timezone: Some("UTC".to_string()),
            locale: Some("en".to_string()),
            preferences: [("theme".to_string(), "light".to_string())].into(),
            categories: vec!["general".to_string()],
            tags: vec!["user".to_string()],
        },
    }
}

/// Helper function to convert session data back to User
fn session_data_to_user(session_data: &rustelo_server::crypto::EncryptedSessionData) -> User {
    let roles: Vec<Role> = session_data
        .roles
        .iter()
        .filter_map(|r| match r.as_str() {
            "Admin" => Some(Role::Admin),
            "Moderator" => Some(Role::Moderator),
            "User" => Some(Role::User),
            "Guest" => Some(Role::Guest),
            _ => None,
        })
        .collect();

    User {
        id: Uuid::parse_str(&session_data.user_id).unwrap_or_default(),
        email: session_data.email.clone(),
        username: session_data.username.clone(),
        display_name: session_data.display_name.clone(),
        avatar_url: None,
        roles,
        is_active: true,
        email_verified: true,
        created_at: Utc::now(),
        updated_at: Utc::now(),
        last_login: Some(Utc::now()),
        two_factor_enabled: false,
        profile: UserProfile {
            first_name: None,
            last_name: None,
            bio: None,
            timezone: None,
            locale: None,
            preferences: session_data.preferences.clone(),
            categories: session_data.categories.clone(),
            tags: session_data.tags.clone(),
        },
    }
}

/// Helper function to check if user is admin
async fn is_admin_user(state: &ServerState, cookies: &Cookies) -> bool {
    if let Some(encrypted_session) = state.session_store.get_session_cookie(cookies) {
        if let Ok(session_data) = state.session_store.get_session(&encrypted_session) {
            return session_data.roles.contains(&"Admin".to_string());
        }
    }
    false
}

/// Helper function to extract cookies from request (for middleware compatibility)
fn get_cookies_from_request(request: &Request) -> Cookies {
    // This is a simplified version - in practice, you'd properly extract cookies
    // from the request headers. For this example, we'll create an empty Cookies instance.
    Cookies::new()
}

/// Environment setup example
pub fn setup_environment_example() {
    // Example environment variables for crypto setup
    std::env::set_var("CRYPTO_KEY", CryptoService::generate_key_base64());
    std::env::set_var("ENVIRONMENT", "development");
    std::env::set_var("SESSION_LIFETIME_HOURS", "24");
    std::env::set_var("ENCRYPTED_CONFIG_FILE", "config/encrypted.json");
    std::env::set_var("COOKIE_DOMAIN", "localhost");
    std::env::set_var("SERVER_HOST", "127.0.0.1");
    std::env::set_var("SERVER_PORT", "3030");

    // Example sensitive config that would be encrypted
    std::env::set_var(
        "DATABASE_URL",
        "postgresql://user:password@localhost/rustelo_dev",
    );
    std::env::set_var("JWT_SECRET", "your-jwt-secret-key");
    std::env::set_var("SMTP_PASSWORD", "your-smtp-password");
    std::env::set_var("GOOGLE_CLIENT_SECRET", "your-google-oauth-secret");
}

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

    #[tokio::test]
    async fn test_server_state_creation() {
        let state = ServerState::new().await;
        assert!(state.is_ok());
    }

    #[tokio::test]
    async fn test_mock_user_creation() {
        let credentials = LoginCredentials {
            email: "test@example.com".to_string(),
            password: "password".to_string(),
            remember_me: false,
        };

        let user = create_mock_user_from_credentials(&credentials);
        assert_eq!(user.email, "test@example.com");
        assert_eq!(user.username, "test");
        assert!(user.roles.contains(&Role::User));
    }

    #[tokio::test]
    async fn test_session_data_conversion() {
        let session_data = crate::crypto::EncryptedSessionData {
            user_id: Uuid::new_v4().to_string(),
            email: "test@example.com".to_string(),
            username: "testuser".to_string(),
            display_name: Some("Test User".to_string()),
            roles: vec!["User".to_string(), "Admin".to_string()],
            categories: vec!["tech".to_string()],
            tags: vec!["rust".to_string()],
            preferences: std::collections::HashMap::new(),
            created_at: Utc::now().timestamp(),
            expires_at: Utc::now().timestamp() + 3600,
        };

        let user = session_data_to_user(&session_data);
        assert_eq!(user.email, "test@example.com");
        assert_eq!(user.username, "testuser");
        assert_eq!(user.roles.len(), 2);
        assert!(user.profile.categories.contains(&"tech".to_string()));
        assert!(user.profile.tags.contains(&"rust".to_string()));
    }

    #[tokio::test]
    async fn test_public_routes() {
        setup_environment_example();
        let state = ServerState::new().await.unwrap();
        let app = create_app(state);

        // Test root route
        let response = app
            .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
            .await
            .unwrap();

        assert_eq!(response.status(), StatusCode::OK);
    }
}
