//! HTTP integration tests for Epoch endpoint operations.
//!
//! Coverage:
//! - POST /v1/epoch - Epoch creation with validation
//! - Epoch name validation (empty, whitespace)
//! - Supersession validation (supersedes hash + supersession_type)
//! - Hex validation for supersedes field
//! - Deterministic ID generation (content-addressed)
//! - Default timestamp handling

#![allow(clippy::expect_used)]

mod common;

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

use stemedb_api::create_router;

// ============================================================================
// Epoch Creation Tests
// ============================================================================

#[tokio::test]
async fn test_create_epoch_success() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    let epoch = json!({
        "name": "GAAP-2024",
        "start_timestamp": 1704067200
    });

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

    let response = app.oneshot(request).await.expect("Request");
    let status = response.status();

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

    assert_eq!(status, StatusCode::CREATED, "Body: {:?}", json);

    assert_eq!(json["status"], "created");
    // BLAKE3 hash = 32 bytes = 64 hex characters
    assert_eq!(json["hash"].as_str().expect("hash").len(), 64);
}

// ============================================================================
// Epoch Name Validation Tests
// ============================================================================

#[tokio::test]
async fn test_create_epoch_empty_name_fails() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    let epoch = json!({
        "name": "",
        "start_timestamp": 1704067200
    });

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

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

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    assert!(json["error"].as_str().expect("error").contains("cannot be empty"));
}

#[tokio::test]
async fn test_create_epoch_whitespace_name_fails() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    let epoch = json!({
        "name": "   ",
        "start_timestamp": 1704067200
    });

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

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

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    assert!(json["error"].as_str().expect("error").contains("cannot be empty"));
}

// ============================================================================
// Epoch Supersession Validation Tests
// ============================================================================

#[tokio::test]
async fn test_create_epoch_with_supersedes() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    // Supersession requires both supersedes hash AND supersession_type
    let epoch = json!({
        "name": "GAAP-2025",
        "supersedes": "a".repeat(64),
        "supersession_type": "Temporal",
        "start_timestamp": 1735689600
    });

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

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

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    assert_eq!(json["status"], "created");
}

#[tokio::test]
async fn test_create_epoch_supersedes_without_type_fails() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    // Providing supersedes but NOT supersession_type should fail
    let epoch = json!({
        "name": "GAAP-2025",
        "supersedes": "a".repeat(64),
        // Missing supersession_type!
        "start_timestamp": 1735689600
    });

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

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

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    assert!(json["error"].as_str().expect("error").contains("supersession_type is required"));
}

#[tokio::test]
async fn test_create_epoch_invalid_supersedes_hex() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    let epoch = json!({
        "name": "GAAP-2025",
        "supersedes": "invalid",  // Too short, should be 64 hex chars
        "supersession_type": "Invalidate",
        "start_timestamp": 1735689600
    });

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

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

    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    // Should complain about hex length
    assert!(json["error"].as_str().expect("error").contains("Expected 64 hex characters"));
}

// ============================================================================
// Epoch Content-Addressed ID Tests
// ============================================================================

#[tokio::test]
async fn test_create_epoch_deterministic_id() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    // Create the same epoch twice - should get the same ID
    let epoch = json!({
        "name": "Deterministic-Test",
        "start_timestamp": 1000
    });

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

    let response1 = app.clone().oneshot(request1).await.expect("Request");
    assert_eq!(response1.status(), StatusCode::CREATED);
    let body1 = axum::body::to_bytes(response1.into_body(), usize::MAX).await.expect("Body");
    let json1: serde_json::Value = serde_json::from_slice(&body1).expect("JSON");
    let hash1 = json1["hash"].as_str().expect("hash");

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

    let response2 = app.oneshot(request2).await.expect("Request");
    assert_eq!(response2.status(), StatusCode::CREATED);
    let body2 = axum::body::to_bytes(response2.into_body(), usize::MAX).await.expect("Body");
    let json2: serde_json::Value = serde_json::from_slice(&body2).expect("JSON");
    let hash2 = json2["hash"].as_str().expect("hash");

    // Same input should produce same ID
    assert_eq!(hash1, hash2, "Same epoch input should produce deterministic ID");
}

// ============================================================================
// Epoch Timestamp Default Tests
// ============================================================================

#[tokio::test]
async fn test_create_epoch_defaults_timestamp() {
    let env = common::create_test_env().await;
    let app = create_router(env.state);

    // Omit start_timestamp - should default to current time
    let epoch = json!({
        "name": "Default-Timestamp-Test"
    });

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

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

    // If we got 201, the timestamp defaulted successfully
    let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.expect("Body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("JSON");
    assert_eq!(json["status"], "created");
}
