//! HTTP integration tests for full pipeline roundtrip.
//!
//! Coverage:
//! - Full assertion roundtrip: POST → Ingest → GET
//! - Integration with Ingestor for processing WAL entries
//! - Signature verification through the pipeline
//! - Query results after ingestion
//!
//! These tests validate the complete data flow through the system:
//! 1. Client POSTs assertion to /v1/assert (writes to WAL)
//! 2. Ingestor processes WAL entries (signature verification, storage)
//! 3. Client GETs via /v1/query (reads from indexed storage)

#![allow(clippy::expect_used)]

mod common;

use axum::{
    body::Body,
    http::{Request, StatusCode},
};
use tower::ServiceExt;

use stemedb_api::create_router;

// ============================================================================
// Full Pipeline Integration Tests (POST → Ingest → GET roundtrip)
// ============================================================================

/// Test full assertion roundtrip: POST → Ingest → GET
///
/// This test validates the complete pipeline:
/// 1. POST an assertion to /v1/assert (writes to WAL)
/// 2. Run the ingestor to process the WAL entry
/// 3. GET /v1/query to verify the assertion is queryable
#[tokio::test]
async fn test_assertion_roundtrip_with_ingestor() {
    let env = common::create_test_env_with_ingestor().await;
    let app = create_router(env.state.clone());

    let subject = "RoundtripTest_Entity";
    let predicate = "test_property";
    let value = 42.0;

    // 1. POST the assertion
    let assertion = common::create_signed_assertion_json(subject, predicate, value);

    let request = Request::builder()
        .uri("/v1/assert")
        .method("POST")
        .header("content-type", "application/json")
        .body(Body::from(serde_json::to_vec(&assertion).expect("JSON")))
        .expect("Request");

    let response = app.clone().oneshot(request).await.expect("Request");
    assert_eq!(response.status(), StatusCode::CREATED, "POST should succeed with 201");

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let post_result: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    assert_eq!(post_result["status"], "created");
    let assertion_hash = post_result["hash"].as_str().expect("hash should be present");
    assert_eq!(assertion_hash.len(), 64, "hash should be 64 hex chars");

    // 2. Run the ingestor to process the pending record (synchronous for testing)
    let bytes_processed = env.ingestor.process_pending().await.expect("ingestor should process");
    assert!(bytes_processed > 0, "Should have processed WAL bytes");

    // 3. GET the assertion via query
    let request = Request::builder()
        .uri(format!("/v1/query?subject={}&predicate={}", subject, predicate))
        .method("GET")
        .body(Body::empty())
        .expect("Request");

    let response = app.oneshot(request).await.expect("Request");
    assert_eq!(response.status(), StatusCode::OK);

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let query_result: serde_json::Value = serde_json::from_slice(&body).expect("JSON");

    // Verify the assertion is returned correctly
    assert_eq!(query_result["total_count"], 1, "Should find exactly 1 assertion");
    let assertions = query_result["assertions"].as_array().expect("assertions array");
    assert_eq!(assertions.len(), 1);

    let found = &assertions[0];
    assert_eq!(found["subject"], subject);
    assert_eq!(found["predicate"], predicate);
    assert_eq!(found["object"]["type"], "Number");
    assert!((found["object"]["value"].as_f64().expect("value") - value).abs() < 0.001);
    assert_eq!(found["confidence"], 0.95);
}

/// Test multiple assertions through the pipeline
#[tokio::test]
async fn test_multiple_assertions_roundtrip() {
    let env = common::create_test_env_with_ingestor().await;
    let app = create_router(env.state.clone());

    let subject = "MultiTest_Entity";

    // POST multiple assertions for the same subject
    for i in 1..=3 {
        let predicate = format!("property_{}", i);
        let value = i as f64 * 10.0;
        let assertion = common::create_signed_assertion_json(subject, &predicate, value);

        let request = Request::builder()
            .uri("/v1/assert")
            .method("POST")
            .header("content-type", "application/json")
            .body(Body::from(serde_json::to_vec(&assertion).expect("JSON")))
            .expect("Request");

        let response = app.clone().oneshot(request).await.expect("Request");
        assert_eq!(response.status(), StatusCode::CREATED, "POST {} should succeed", i);
    }

    // Process all pending WAL entries
    let bytes_processed = env.ingestor.process_pending().await.expect("ingestor should process");
    assert!(bytes_processed > 0, "Should have processed WAL bytes");

    // Query by subject only - should get all 3 assertions
    let request = Request::builder()
        .uri(format!("/v1/query?subject={}", subject))
        .method("GET")
        .body(Body::empty())
        .expect("Request");

    let response = app.oneshot(request).await.expect("Request");
    assert_eq!(response.status(), StatusCode::OK);

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let query_result: serde_json::Value = serde_json::from_slice(&body).expect("JSON");

    assert_eq!(query_result["total_count"], 3, "Should find all 3 assertions");
    let assertions = query_result["assertions"].as_array().expect("assertions array");
    assert_eq!(assertions.len(), 3);

    // Verify all predicates are present
    let predicates: Vec<String> = assertions
        .iter()
        .map(|a| a["predicate"].as_str().expect("predicate").to_string())
        .collect();
    assert!(predicates.contains(&"property_1".to_string()));
    assert!(predicates.contains(&"property_2".to_string()));
    assert!(predicates.contains(&"property_3".to_string()));
}
