/*-------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation.  All rights reserved.
 *
 * documentdb_gateway_core/src/testing/connection_context.rs
 *
 * Shared connection-context construction helpers for unit tests.
 *
 *-------------------------------------------------------------------------
 */

use std::sync::Arc;

use tokio::sync::OnceCell;
use uuid::Uuid;

use crate::{
    configuration::{
        CertInputType, CertificateOptions, DocumentDBSetupConfiguration, DynamicConfiguration,
        SetupConfiguration,
    },
    context::{ConnectionContext, ServiceContext},
    postgres::{
        conn_mgmt::{
            ConnectionPool, PgPoolSettings, PoolManager, AUTHENTICATION_MAX_CONNECTIONS,
            SYSTEM_REQUESTS_MAX_CONNECTIONS,
        },
        create_query_catalog,
    },
    service::TlsProvider,
    telemetry::{MetricsOptions, TelemetryOptions, TelemetryProvider},
};

pub async fn test_connection_context(
    metrics_enabled: bool,
    dynamic_configuration: Arc<dyn DynamicConfiguration>,
    telemetry_provider: Option<Box<dyn TelemetryProvider>>,
) -> ConnectionContext {
    let setup_configuration = test_setup_configuration(metrics_enabled);
    let query_catalog = create_query_catalog();
    let application_name = setup_configuration.application_name().to_owned();
    let postgres_system_user = setup_configuration.postgres_system_user.clone();

    let system_requests_pool = ConnectionPool::new_with_user(
        &setup_configuration,
        &query_catalog,
        &postgres_system_user,
        None,
        &format!("{application_name}-SystemRequests"),
        PgPoolSettings::system_pool_settings(SYSTEM_REQUESTS_MAX_CONNECTIONS),
    )
    .expect("system requests pool should initialize for tests");

    let authentication_pool = ConnectionPool::new_with_user(
        &setup_configuration,
        &query_catalog,
        &postgres_system_user,
        None,
        &format!("{application_name}-PreAuthRequests"),
        PgPoolSettings::system_pool_settings(AUTHENTICATION_MAX_CONNECTIONS),
    )
    .expect("authentication pool should initialize for tests");

    let pool_manager = Arc::new(PoolManager::new(
        query_catalog,
        Box::new(setup_configuration.clone()),
        system_requests_pool,
        authentication_pool,
    ));
    let service_context = ServiceContext::new(
        Box::new(setup_configuration),
        dynamic_configuration,
        pool_manager,
        test_tls_provider().await,
        None,
    );

    ConnectionContext::new(
        service_context,
        telemetry_provider,
        "10.0.0.1".to_owned(),
        None,
        Uuid::from_u128(1),
        "TCP".to_owned(),
    )
}

async fn test_tls_provider() -> TlsProvider {
    static TLS_PROVIDER: OnceCell<TlsProvider> = OnceCell::const_new();

    TLS_PROVIDER
        .get_or_init(|| async {
            TlsProvider::new(
                &CertificateOptions {
                    cert_type: CertInputType::PemAutoGenerated,
                    ..Default::default()
                },
                None,
                None,
            )
            .await
            .expect("test TLS provider should initialize")
        })
        .await
        .clone()
}

fn test_setup_configuration(metrics_enabled: bool) -> DocumentDBSetupConfiguration {
    let system_user = std::env::var("PostgresSystemUser").unwrap_or_else(|_| whoami::username());

    DocumentDBSetupConfiguration {
        node_host_name: "localhost".to_owned(),
        blocked_role_prefixes: Vec::new(),
        gateway_listen_port: Some(10260),
        allow_transaction_snapshot: Some(false),
        certificate_options: CertificateOptions {
            cert_type: CertInputType::PemAutoGenerated,
            ..Default::default()
        },
        postgres_system_user: system_user.clone(),
        postgres_data_user: system_user,
        telemetry_options: Some(TelemetryOptions {
            metrics: Some(MetricsOptions {
                enabled: Some(metrics_enabled),
                ..Default::default()
            }),
            ..Default::default()
        }),
        ..Default::default()
    }
}
