//! Saga single-step failure scenario tests.
//!
//! Split from `federation_saga_e2e_scenarios.rs`:
//! - Single-step failure scenarios (6 tests)

#![allow(clippy::cast_possible_truncation)] // Reason: test step counts cast usize→u32; test sizes never exceed u32::MAX
#![allow(clippy::unwrap_used, clippy::panic)] // Reason: test code, panics acceptable
use fraiseql_core::federation::{
    saga_coordinator::{CompensationStrategy, SagaCoordinator},
    saga_executor::SagaExecutor,
};

use super::common;

// ===========================================================================================
// SINGLE-STEP FAILURE SCENARIOS
// ===========================================================================================

#[tokio::test]
async fn test_first_step_failure_prevents_second_step() {
    // Given: A saga with 5 steps where step 1 will fail
    let scenario = common::TestSagaScenario::new(5);
    let (_, saga_id) = common::execute_saga_scenario(scenario).await;

    // When: Step 1 fails
    common::execute_all_steps_with_failure(saga_id, 5, Some(1)).await;

    // Then: Steps 2-5 should not execute
    // Verify by attempting to execute step 2 (it should not have been executed automatically)
    let executor = SagaExecutor::new();
    let result = executor
        .execute_step(saga_id, 2, "mutation2", &serde_json::json!({"step": 2}), "service-2")
        .await;

    // The step can execute (placeholder), but in a real implementation,
    // it would be blocked because step 1 failed
    result.unwrap_or_else(|e| panic!("execute_step(2) after step 1 failure failed: {e}"));
}

#[tokio::test]
async fn test_middle_step_failure_stops_subsequent_steps() {
    // Given: A saga with 5 steps where step 3 will fail
    let scenario = common::TestSagaScenario::new(5);
    let (_, saga_id) = common::execute_saga_scenario(scenario).await;

    // When: Execute steps 1-2 successfully
    common::execute_all_steps_with_failure(saga_id, 5, Some(3)).await;

    let executor = SagaExecutor::new();

    // Then: Step 1 should have executed
    let result1 = executor
        .execute_step(saga_id, 1, "mutation1", &serde_json::json!({"step": 1}), "service-1")
        .await;
    let result1 = result1.unwrap_or_else(|e| panic!("execute_step(1) failed: {e}"));

    // Step 2 should have executed
    let result2 = executor
        .execute_step(saga_id, 2, "mutation2", &serde_json::json!({"step": 2}), "service-2")
        .await;
    let result2 = result2.unwrap_or_else(|e| panic!("execute_step(2) failed: {e}"));

    // Step 3 and beyond would not execute in real implementation
    // Verify the failure point is at step 3
    assert_eq!(result1.step_number, 1);
    assert_eq!(result2.step_number, 2);
}

#[tokio::test]
async fn test_last_step_failure_triggers_compensation() {
    // Given: A saga with 4 steps where step 4 will fail
    let coordinator = SagaCoordinator::new(CompensationStrategy::Automatic);
    let steps = common::TestSagaScenario::new(4).build_steps();
    let saga_id = coordinator.create_saga(steps).await.expect("Failed to create saga");

    // When: Execute through step 4 (which fails)
    common::execute_all_steps_with_failure(saga_id, 4, Some(4)).await;

    // Then: Compensation should be triggered
    // In automatic strategy, compensation should begin automatically
    // For now, we verify the saga can detect the failed state
    let status = coordinator.get_saga_status(saga_id).await.expect("Failed to get status");

    // Saga should still be queryable (status available)
    assert_eq!(status.saga_id, saga_id);
}

#[tokio::test]
async fn test_aborted_saga_transitions_to_aborted_state() {
    // Given: A saga with 3 steps
    let coordinator = SagaCoordinator::new(CompensationStrategy::Automatic);
    let steps = common::TestSagaScenario::new(3).build_steps();
    let saga_id = coordinator.create_saga(steps).await.expect("Failed to create saga");

    // When: Step 2 fails during execution
    common::execute_all_steps_with_failure(saga_id, 3, Some(2)).await;

    // Then: Saga state should reflect the failure
    let status = coordinator.get_saga_status(saga_id).await.expect("Failed to get status");

    // Status should be available (in real implementation, state would be Failed)
    assert_eq!(status.saga_id, saga_id);
    // completed_steps should reflect: only step 1 completed before failure
    assert!(status.completed_steps <= 1);
}

#[tokio::test]
async fn test_failure_error_message_includes_step_context() {
    // Given: A saga with 4 steps
    let scenario = common::TestSagaScenario::new(4);
    let (steps, saga_id) = common::execute_saga_scenario(scenario).await;

    // When: Step 2 fails with error
    common::execute_all_steps_with_failure(saga_id, 4, Some(2)).await;

    // Then: Error should include context about the failed step
    // Verify the failure happened at the correct step
    let _executor = SagaExecutor::new();
    let step_info = &steps[1]; // Step 2 (0-indexed)

    // In a real scenario with failure injection, we'd see:
    // - Step number (2)
    // - Mutation name (mutation2)
    // - Subgraph name (service-2 or service-3)
    // - Error message describing the failure

    assert_eq!(step_info.number, 2);
    assert_eq!(step_info.mutation_name, "mutation2");
}

#[tokio::test]
async fn test_failure_records_completed_steps_count() {
    // Given: A saga with 5 steps
    let coordinator = SagaCoordinator::new(CompensationStrategy::Automatic);
    let steps = common::TestSagaScenario::new(5).build_steps();
    let saga_id = coordinator.create_saga(steps).await.expect("Failed to create saga");

    // When: Step 3 fails (after steps 1 and 2 completed)
    common::execute_all_steps_with_failure(saga_id, 5, Some(3)).await;

    // Then: Completed steps count should be 2
    let status = coordinator.get_saga_status(saga_id).await.expect("Failed to get status");

    // In real implementation, completed_steps would be 2 (steps 1 and 2 succeeded)
    // For now, verify we can query the status
    assert_eq!(status.saga_id, saga_id);
    // This would be the assertion in full implementation:
    // assert_eq!(status.completed_steps, 2);
}
