//! Database abstraction usage examples
//!
//! This module demonstrates how to use the database abstraction layer
//! for both PostgreSQL and SQLite databases with the same code.

use anyhow::Result;
use chrono::Utc;
use uuid::Uuid;

use crate::database::{
    DatabaseConfig, DatabasePool, DatabaseType,
    auth::{AuthRepository, AuthRepositoryTrait, CreateUserRequest, DatabaseUser},
    connection::DatabaseConnection,
};

/// Example: Creating and using database connections
pub async fn database_connection_example() -> Result<()> {
    println!("=== Database Connection Example ===");

    // Example 1: SQLite connection (for development/testing)
    let sqlite_config = DatabaseConfig {
        url: "sqlite:data/example.db".to_string(),
        max_connections: 5,
        min_connections: 1,
        connect_timeout: std::time::Duration::from_secs(30),
        idle_timeout: std::time::Duration::from_secs(600),
        max_lifetime: std::time::Duration::from_secs(1800),
    };

    let sqlite_pool = DatabasePool::new(&sqlite_config).await?;
    println!("✓ Created SQLite connection pool");

    // Example 2: PostgreSQL connection (for production)
    // Uncomment to test with actual PostgreSQL database
    /*
    let postgres_config = DatabaseConfig {
        url: "postgresql://user:password@localhost:5432/myapp".to_string(),
        max_connections: 20,
        min_connections: 5,
        connect_timeout: std::time::Duration::from_secs(30),
        idle_timeout: std::time::Duration::from_secs(600),
        max_lifetime: std::time::Duration::from_secs(1800),
    };

    let postgres_pool = DatabasePool::new(&postgres_config).await?;
    println!("✓ Created PostgreSQL connection pool");
    */

    // Create database connections
    let sqlite_conn = sqlite_pool.create_connection();
    println!("✓ Created SQLite database connection");

    // Show database type detection
    match sqlite_conn.database_type() {
        DatabaseType::SQLite => println!("✓ Detected SQLite database"),
        DatabaseType::PostgreSQL => println!("✓ Detected PostgreSQL database"),
    }

    Ok(())
}

/// Example: Using the authentication repository with database abstraction
pub async fn auth_repository_example() -> Result<()> {
    println!("\n=== Authentication Repository Example ===");

    // Create SQLite database for this example
    let config = DatabaseConfig {
        url: "sqlite:data/auth_example.db".to_string(),
        max_connections: 5,
        min_connections: 1,
        connect_timeout: std::time::Duration::from_secs(30),
        idle_timeout: std::time::Duration::from_secs(600),
        max_lifetime: std::time::Duration::from_secs(1800),
    };

    let pool = DatabasePool::new(&config).await?;
    let auth_repo = AuthRepository::from_pool(&pool);

    // Initialize database tables
    auth_repo.init_tables().await?;
    println!("✓ Initialized authentication tables");

    // Create a new user
    let user_request = CreateUserRequest {
        email: "alice@example.com".to_string(),
        username: Some("alice".to_string()),
        display_name: Some("Alice Johnson".to_string()),
        password_hash: "hashed_password_here".to_string(), // In real app, use proper hashing
        is_verified: false,
        is_active: true,
    };

    let created_user = auth_repo.create_user(&user_request).await?;
    println!(
        "✓ Created user: {} (ID: {})",
        created_user.email, created_user.id
    );

    // Find user by email
    let found_user = auth_repo.find_user_by_email("alice@example.com").await?;

    match found_user {
        Some(user) => {
            println!("✓ Found user by email: {}", user.email);
            println!("  - Username: {:?}", user.username);
            println!("  - Display name: {:?}", user.display_name);
            println!("  - Active: {}", user.is_active);
            println!("  - Verified: {}", user.is_verified);
        }
        None => println!("✗ User not found"),
    }

    // Find user by ID
    let found_by_id = auth_repo.find_user_by_id(&created_user.id).await?;

    match found_by_id {
        Some(user) => println!("✓ Found user by ID: {}", user.email),
        None => println!("✗ User not found by ID"),
    }

    Ok(())
}

/// Example: Database-agnostic user operations
pub async fn database_agnostic_example() -> Result<()> {
    println!("\n=== Database-Agnostic Operations Example ===");

    // This function works with any database backend
    async fn perform_user_operations(auth_repo: &AuthRepository) -> Result<DatabaseUser> {
        // The same code works with both PostgreSQL and SQLite!
        let user_request = CreateUserRequest {
            email: "bob@example.com".to_string(),
            username: Some("bob".to_string()),
            display_name: Some("Bob Smith".to_string()),
            password_hash: "another_hashed_password".to_string(),
            is_verified: true,
            is_active: true,
        };

        let user = auth_repo.create_user(&user_request).await?;
        println!(
            "  ✓ Created user in {} database",
            match auth_repo.database_type() {
                DatabaseType::PostgreSQL => "PostgreSQL",
                DatabaseType::SQLite => "SQLite",
            }
        );

        Ok(user)
    }

    // Test with SQLite
    let sqlite_config = DatabaseConfig {
        url: "sqlite:data/agnostic_example.db".to_string(),
        max_connections: 5,
        min_connections: 1,
        connect_timeout: std::time::Duration::from_secs(30),
        idle_timeout: std::time::Duration::from_secs(600),
        max_lifetime: std::time::Duration::from_secs(1800),
    };

    let sqlite_pool = DatabasePool::new(&sqlite_config).await?;
    let sqlite_auth = AuthRepository::from_pool(&sqlite_pool);
    sqlite_auth.init_tables().await?;

    let _sqlite_user = perform_user_operations(&sqlite_auth).await?;

    // The exact same function would work with PostgreSQL:
    /*
    let postgres_config = DatabaseConfig {
        url: "postgresql://user:password@localhost:5432/myapp".to_string(),
        // ... other config
    };
    let postgres_pool = DatabasePool::new(&postgres_config).await?;
    let postgres_auth = AuthRepository::from_pool(&postgres_pool);
    postgres_auth.init_tables().await?;

    let _postgres_user = perform_user_operations(&postgres_auth).await?;
    */

    println!("✓ Same code works with different database backends!");

    Ok(())
}

/// Example: Converting between database and shared user types
pub async fn user_conversion_example() -> Result<()> {
    println!("\n=== User Type Conversion Example ===");

    // Create a database user
    let db_user = DatabaseUser {
        id: Uuid::new_v4(),
        email: "charlie@example.com".to_string(),
        username: Some("charlie".to_string()),
        display_name: Some("Charlie Brown".to_string()),
        password_hash: "secure_hash".to_string(),
        avatar_url: Some("https://example.com/avatar.jpg".to_string()),
        roles: vec!["user".to_string(), "moderator".to_string()],
        is_active: true,
        is_verified: true,
        email_verified: true,
        created_at: Utc::now(),
        updated_at: Utc::now(),
        last_login: None,
        two_factor_enabled: false,
        two_factor_secret: None,
        backup_codes: Vec::new(),
    };

    println!("✓ Created DatabaseUser with {} roles", db_user.roles.len());

    // Convert to shared User type (for API responses, etc.)
    let shared_user: rustelo_core_lib::auth::User = db_user.into();
    println!("✓ Converted to rustelo_core_lib::auth::User");
    println!("  - Email: {}", shared_user.email);
    println!("  - Username: {}", shared_user.username);
    println!("  - Roles: {:?}", shared_user.roles);

    // Convert back to DatabaseUser (when needed for database operations)
    let back_to_db: DatabaseUser = shared_user.into();
    println!("✓ Converted back to DatabaseUser");
    println!("  - Email: {}", back_to_db.email);
    println!("  - Username: {:?}", back_to_db.username);

    Ok(())
}

/// Run all database abstraction examples
pub async fn run_all_examples() -> Result<()> {
    println!("🚀 Running Database Abstraction Examples\n");

    database_connection_example().await?;
    auth_repository_example().await?;
    database_agnostic_example().await?;
    user_conversion_example().await?;

    println!("\n✅ All examples completed successfully!");
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_database_abstraction() -> Result<()> {
        // Test that we can create connections and repositories
        let config = DatabaseConfig {
            url: "sqlite::memory:".to_string(),
            max_connections: 1,
            min_connections: 1,
            connect_timeout: std::time::Duration::from_secs(30),
            idle_timeout: std::time::Duration::from_secs(600),
            max_lifetime: std::time::Duration::from_secs(1800),
        };

        let pool = DatabasePool::new(&config).await?;
        let auth_repo = AuthRepository::from_pool(&pool);

        // Test table initialization
        auth_repo.init_tables().await?;

        // Test user creation
        let user_request = CreateUserRequest {
            email: "test@example.com".to_string(),
            username: Some("testuser".to_string()),
            display_name: Some("Test User".to_string()),
            password_hash: "test_hash".to_string(),
            is_verified: false,
            is_active: true,
        };

        let user = auth_repo.create_user(&user_request).await?;
        assert_eq!(user.email, "test@example.com");
        assert_eq!(user.username, Some("testuser".to_string()));

        // Test user retrieval
        let found_user = auth_repo.find_user_by_email("test@example.com").await?;
        assert!(found_user.is_some());

        let found_by_id = auth_repo.find_user_by_id(&user.id).await?;
        assert!(found_by_id.is_some());

        Ok(())
    }

    #[test]
    fn test_user_conversions() {
        let db_user = DatabaseUser {
            id: Uuid::new_v4(),
            email: "test@example.com".to_string(),
            username: Some("testuser".to_string()),
            display_name: Some("Test User".to_string()),
            password_hash: "hash".to_string(),
            avatar_url: None,
            roles: vec!["user".to_string()],
            is_active: true,
            is_verified: true,
            email_verified: true,
            created_at: Utc::now(),
            updated_at: Utc::now(),
            last_login: None,
            two_factor_enabled: false,
            two_factor_secret: None,
            backup_codes: Vec::new(),
        };

        // Test conversion to shared type
        let shared_user: rustelo_core_lib::auth::User = db_user.clone().into();
        assert_eq!(shared_user.email, "test@example.com");
        assert_eq!(shared_user.username, "testuser");

        // Test conversion back
        let back_to_db: DatabaseUser = shared_user.into();
        assert_eq!(back_to_db.email, "test@example.com");
        assert_eq!(back_to_db.username, Some("testuser".to_string()));
    }
}
