//! Behavioral tests for observability subsystems.
//!
//! Tests real production types:
//! - `MetricsCollector` atomic counters
//! - `tracing_utils::extract_trace_context` header parsing
//!
//! **Execution engine:** none
//! **Infrastructure:** none
//! **Parallelism:** safe
#![allow(clippy::unwrap_used)] // Reason: test code, panics acceptable
#![allow(clippy::cast_precision_loss)] // Reason: test metrics reporting
#![allow(clippy::cast_sign_loss)] // Reason: test data uses small positive integers
#![allow(clippy::cast_possible_truncation)] // Reason: test data values are bounded
#![allow(clippy::cast_possible_wrap)] // Reason: test data values are bounded
#![allow(clippy::cast_lossless)] // Reason: test code readability
#![allow(clippy::missing_panics_doc)] // Reason: test helper functions
#![allow(clippy::missing_errors_doc)] // Reason: test helper functions
#![allow(missing_docs)] // Reason: test code
#![allow(clippy::items_after_statements)] // Reason: test helpers near use site
#![allow(clippy::used_underscore_binding)] // Reason: test variables use _ prefix
#![allow(clippy::needless_pass_by_value)] // Reason: test helper signatures
#![allow(clippy::match_same_arms)] // Reason: test data clarity
#![allow(clippy::branches_sharing_code)] // Reason: test assertion clarity
#![allow(clippy::undocumented_unsafe_blocks)] // Reason: test exercises unsafe paths

mod common;

use std::sync::atomic::Ordering;

use common::test_app::{health_router, make_test_state};
use fraiseql_server::MetricsCollector;
use tower::ServiceExt;

// ============================================================================
// METRICS COLLECTOR
// ============================================================================

#[test]
fn metrics_collector_starts_at_zero() {
    let metrics = MetricsCollector::new();
    assert_eq!(metrics.queries_total.load(Ordering::Relaxed), 0);
    assert_eq!(metrics.queries_success.load(Ordering::Relaxed), 0);
    assert_eq!(metrics.queries_error.load(Ordering::Relaxed), 0);
    assert_eq!(metrics.queries_duration_us.load(Ordering::Relaxed), 0);
}

#[test]
fn metrics_collector_increments_atomically() {
    let metrics = MetricsCollector::new();
    metrics.queries_total.fetch_add(1, Ordering::Relaxed);
    metrics.queries_total.fetch_add(1, Ordering::Relaxed);
    metrics.queries_total.fetch_add(1, Ordering::Relaxed);

    assert_eq!(metrics.queries_total.load(Ordering::Relaxed), 3);
}

#[test]
fn metrics_collector_arc_shares_state() {
    // F009: MetricsCollector no longer implements Clone directly; per-field
    // Arc<AtomicU64> was a redundant indirection because the collector is
    // always wrapped in Arc<MetricsCollector> at the use site. Cloning the
    // outer Arc gives the same "shared mutable counters" behaviour that the
    // old per-field Arc derivation provided.
    let metrics = std::sync::Arc::new(MetricsCollector::new());
    let clone = std::sync::Arc::clone(&metrics);

    metrics.queries_total.fetch_add(5, Ordering::Relaxed);
    assert_eq!(
        clone.queries_total.load(Ordering::Relaxed),
        5,
        "Arc<MetricsCollector> clones must share the counter state"
    );
}

#[test]
fn metrics_collector_records_entity_resolution() {
    let metrics = MetricsCollector::new();
    metrics.record_entity_resolution(1000, true);
    metrics.record_entity_resolution(2000, false);

    assert_eq!(metrics.federation_entity_resolutions_total.load(Ordering::Relaxed), 2);
    assert_eq!(metrics.federation_entity_resolutions_errors.load(Ordering::Relaxed), 1);
    assert_eq!(metrics.federation_entity_resolution_duration_us.load(Ordering::Relaxed), 3000);
}

// ============================================================================
// TRACING HEADER INTEGRATION
// ============================================================================

#[tokio::test]
async fn request_with_traceparent_header_succeeds() {
    let router = health_router(make_test_state());
    let response = router
        .clone()
        .oneshot(
            http::Request::builder()
                .uri("/health")
                .header("traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
                .body(axum::body::Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(response.status(), http::StatusCode::OK);
}
