//! Authentication services initialization
//!
//! This module handles the initialization of all authentication-related
//! services including JWT, OAuth, password handling, two-factor, and OTP.

#[cfg(feature = "auth")]
use std::sync::Arc;

#[cfg(feature = "auth")]
use crate::auth::otp::OtpService;
#[cfg(all(feature = "auth", feature = "email"))]
use crate::auth::otp_store::{MemoryOtpStore, OtpStore};
#[cfg(feature = "auth")]
use crate::auth::{AuthService, JwtService, OAuthService, PasswordService, TwoFactorService};
#[cfg(feature = "auth")]
use crate::config::load_auth_otp_config;
use crate::config::Config;
#[cfg(all(feature = "auth", feature = "email"))]
use crate::config::OtpStoreKind;
#[cfg(feature = "auth")]
use crate::database::auth::AuthRepositoryTrait;
#[cfg(any(feature = "auth", feature = "content-db"))]
use crate::database::DatabasePool;
#[cfg(all(feature = "auth", feature = "email"))]
use crate::email::EmailService;

/// Initialize authentication services.
///
/// `email_service` is `Some` when the `email` feature is enabled and the
/// caller has already constructed an `EmailService` via
/// `initialize_email_service`. When present, the OTP service is activated
/// automatically.
#[cfg(feature = "auth")]
pub async fn initialize_auth_services(
    database_pool: &DatabasePool,
    config: &Config,
    #[cfg(feature = "email")] email_service: Option<Arc<EmailService>>,
) -> Result<
    (
        Arc<AuthService>,
        Arc<crate::database::auth::AuthRepository>,
        Arc<JwtService>,
    ),
    Box<dyn std::error::Error>,
> {
    let jwt_service = Arc::new(
        JwtService::new()
            .inspect_err(|e| tracing::error!("Failed to create JWT service: {}", e))?,
    );
    let oauth_service = Arc::new(OAuthService::new().map_err(
        |e| -> Box<dyn std::error::Error> {
            tracing::error!("Failed to create OAuth service: {}", e);
            std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into()
        },
    )?);
    let password_service = Arc::new(PasswordService::new());
    let two_factor_service = Arc::new(TwoFactorService::from_pool(
        database_pool,
        config.app.name.clone(),
        format!("{} Authentication", config.app.name),
    ));

    // Load OTP config from site/config/auth.toml (defaults on missing file).
    let auth_toml_path = std::path::Path::new(&config.root_path).join("site/config/auth.toml");
    let otp_config = load_auth_otp_config(&auth_toml_path);

    let auth_repository = Arc::new(
        crate::database::auth::AuthRepository::from_pool(database_pool)
            .with_registration_role(otp_config.registration_role.clone()),
    );

    // Build OTP service when the email feature is enabled and an EmailService
    // was provided.
    #[cfg(feature = "email")]
    let otp_service: Option<Arc<OtpService>> = email_service.map(|email_svc| {
        let store: Arc<dyn OtpStore> = match otp_config.otp_store {
            OtpStoreKind::Memory => MemoryOtpStore::new_arc(),
            OtpStoreKind::Db => {
                tracing::warn!("OTP db store not yet implemented; using memory store");
                MemoryOtpStore::new_arc()
            }
        };
        Arc::new(OtpService {
            store,
            password_svc: password_service.clone(),
            email_svc,
            auth_repo: auth_repository.clone(),
            jwt_svc: jwt_service.clone(),
        })
    });

    #[cfg(not(feature = "email"))]
    let otp_service: Option<Arc<OtpService>> = None;

    let auth_service = Arc::new(AuthService::new(
        jwt_service.clone(),
        oauth_service.clone(),
        password_service.clone(),
        auth_repository.clone(),
        two_factor_service.clone(),
        otp_service,
        otp_config,
    ));

    if let Err(e) = auth_repository.init_tables().await {
        tracing::error!("Failed to initialize database: {}", e);
        std::process::exit(1);
    }

    tracing::info!("Authentication services initialized successfully");
    Ok((auth_service, auth_repository, jwt_service))
}

/// Initialize security components based on configuration
pub fn initialize_security_components(
    config: &Config,
) -> (
    crate::security::csrf::CsrfState,
    crate::security::rate_limit::RateLimiter,
    crate::security::headers::SecurityHeadersConfig,
) {
    use crate::security::{
        csrf::{CsrfConfig, CsrfState},
        headers::SecurityHeadersConfig,
        rate_limit::{RateLimitConfig, RateLimiter},
    };

    let csrf_config = if config.is_production() {
        CsrfConfig {
            secure_cookie: config.session.cookie_secure,
            ..Default::default()
        }
    } else {
        CsrfConfig {
            secure_cookie: false,
            ..Default::default()
        }
    };
    let csrf_state = CsrfState::new(csrf_config);

    let rate_limit_config = RateLimitConfig {
        requests_per_window: config.security.rate_limit_requests,
        window_duration: std::time::Duration::from_secs(config.security.rate_limit_window),
        ..Default::default()
    };
    let rate_limiter = RateLimiter::new(rate_limit_config);

    let security_headers_config = if config.is_production() {
        SecurityHeadersConfig::production()
    } else {
        SecurityHeadersConfig::development()
    };

    (csrf_state, rate_limiter, security_headers_config)
}
