//! Comprehensive saga coordinator testing for distributed transactions:
//! - Saga creation and initialization
//! - Mutation parsing into saga steps
//! - Step execution orchestration
//! - Success path validation
//! - Failure detection and compensation triggering
//! - State machine transitions
//!
//! REFACTOR PHASE: Code is tested, improving design and documentation
//!
//! ## Architecture
//!
//! Saga Coordinator implements distributed transaction orchestration with
//! automatic compensation (rollback) on failure.
//!
//! ### State Machine
//!
//! ```
//! Pending → Executing → Completed (success)
//!          ↓
//!        Failed → Compensating → Compensated (rollback)
//! ```
//!
//! ### Compensation Strategy
//!
//! When a step fails:
//! 1. Mark saga as Compensating
//! 2. Build compensation chain in REVERSE order
//! 3. Execute compensations for completed steps only
//! 4. Mark saga as Compensated
//!
//! Example: If steps [A, B, C] execute with C failing after A and B complete:
//! - Compensation runs: B's inverse, then A's inverse
//! - C has no compensation (never completed)

use serde_json::{Value, json};
use uuid::Uuid;

// ============================================================================
// Test: Saga Coordinator Creation
// ============================================================================

#[test]
fn test_saga_coordinator_creation() {
    // TEST: Create saga coordinator instance
    // GIVEN: No saga coordinator exists
    // WHEN: Creating new coordinator
    // THEN: Should initialize successfully

    let coordinator = SagaCoordinator::new();
    assert!(coordinator.is_ok(), "Should create saga coordinator");
}

#[test]
fn test_saga_id_generation() {
    // TEST: Each saga gets unique ID
    // GIVEN: Saga coordinator created
    // WHEN: Creating multiple sagas
    // THEN: Each saga should have unique UUID

    let saga1 = Saga::new();
    let saga2 = Saga::new();

    assert_ne!(saga1.id, saga2.id, "Saga IDs should be unique");
}

#[test]
fn test_saga_initial_state() {
    // TEST: Saga starts in pending state
    // GIVEN: New saga created
    // WHEN: Checking initial state
    // THEN: Should be in Pending state

    let saga = Saga::new();
    assert_eq!(saga.state, SagaState::Pending, "New saga should be pending");
}

// ============================================================================
// Test: Saga Step Parsing
// ============================================================================

#[test]
fn test_parse_single_step_saga() {
    // TEST: Parse mutation with single step
    // GIVEN: Mutation affecting only one subgraph
    // WHEN: Parsing mutation
    // THEN: Should create single step saga

    let mutation = MutationInfo {
        typename:  "User".to_string(),
        operation: "create".to_string(),
        subgraph:  "users".to_string(),
        variables: json!({"id": "1", "name": "Alice"}),
    };

    let saga = parse_mutation_to_saga(&mutation);
    assert_eq!(saga.steps.len(), 1, "Should have 1 step");
    assert_eq!(saga.steps[0].subgraph, "users");
}

#[test]
fn test_parse_multi_step_saga() {
    // TEST: Parse mutation spanning multiple subgraphs
    // GIVEN: CreateOrderWithInventory mutation
    // WHEN: Parsing
    // THEN: Should create 3 steps (users, orders, products)

    let mutations = vec![
        MutationInfo {
            typename:  "User".to_string(),
            operation: "update".to_string(),
            subgraph:  "users".to_string(),
            variables: json!({"id": "1"}),
        },
        MutationInfo {
            typename:  "Order".to_string(),
            operation: "create".to_string(),
            subgraph:  "orders".to_string(),
            variables: json!({"userId": "1", "items": []}),
        },
        MutationInfo {
            typename:  "Product".to_string(),
            operation: "update".to_string(),
            subgraph:  "products".to_string(),
            variables: json!({"ids": []}),
        },
    ];

    let saga = parse_mutations_to_saga(&mutations);
    assert_eq!(saga.steps.len(), 3, "Should have 3 steps");
    assert_eq!(saga.steps[0].subgraph, "users");
    assert_eq!(saga.steps[1].subgraph, "orders");
    assert_eq!(saga.steps[2].subgraph, "products");
}

#[test]
fn test_saga_step_order_preserved() {
    // TEST: Mutation order is preserved in steps
    // GIVEN: Mutations in specific order
    // WHEN: Parsing
    // THEN: Steps should maintain order

    let mutations = vec![
        MutationInfo {
            typename:  "Order".to_string(),
            operation: "create".to_string(),
            subgraph:  "orders".to_string(),
            variables: json!({}),
        },
        MutationInfo {
            typename:  "Product".to_string(),
            operation: "update".to_string(),
            subgraph:  "products".to_string(),
            variables: json!({}),
        },
    ];

    let saga = parse_mutations_to_saga(&mutations);

    for (index, step) in saga.steps.iter().enumerate() {
        assert_eq!(step.order, index, "Step order should match position");
    }
}

// ============================================================================
// Test: Saga Execution - Success Path
// ============================================================================

#[test]
fn test_saga_execution_success() {
    // TEST: Execute saga with all steps succeeding
    // GIVEN: 3-step saga with mocked successful subgraph responses
    // WHEN: Executing saga
    // THEN: Saga should complete successfully

    let mut saga = create_test_saga_success();
    let result = execute_saga_synchronously(&mut saga);

    assert!(result.is_ok(), "Saga should execute successfully");
    assert_eq!(saga.state, SagaState::Completed, "Saga should be completed");
}

#[test]
fn test_saga_step_execution_order() {
    // TEST: Steps execute in order
    // GIVEN: 3-step saga
    // WHEN: Executing
    // THEN: Should execute steps 0, 1, 2 in sequence

    let saga = create_test_saga_success();
    let execution_order = execute_saga_and_record_order(&saga);

    assert_eq!(execution_order, vec![0, 1, 2], "Steps should execute in order");
}

#[test]
fn test_saga_records_step_result() {
    // TEST: Saga records result of each step
    // GIVEN: Step executes successfully
    // WHEN: Recording result
    // THEN: Saga should store step result

    let mut saga = create_test_saga_success();
    saga.steps[0].result = Some(json!({"id": "user-123"}));

    assert!(saga.steps[0].result.is_some(), "Should record step result");
    assert_eq!(saga.steps[0].result.as_ref().unwrap()["id"], "user-123");
}

#[test]
fn test_saga_execution_time_recorded() {
    // TEST: Record execution time for each step
    // GIVEN: Step executes
    // WHEN: Recording step completion
    // THEN: Should record execution duration

    let mut saga = create_test_saga_success();
    saga.steps[0].started_at = Some(std::time::Instant::now());
    std::thread::sleep(std::time::Duration::from_millis(10));
    saga.steps[0].completed_at = Some(std::time::Instant::now());

    assert!(saga.steps[0].started_at.is_some());
    assert!(saga.steps[0].completed_at.is_some());
}

// ============================================================================
// Test: Saga Execution - Failure & Compensation
// ============================================================================

#[test]
fn test_saga_failure_triggers_compensation() {
    // TEST: Step failure triggers compensation
    // GIVEN: Step 2 fails
    // WHEN: Executing saga
    // THEN: Should switch to compensation mode

    let mut saga = create_test_saga_failure_on_step_2();
    let result = execute_saga_synchronously(&mut saga);

    assert!(result.is_err(), "Saga should fail");
    assert_eq!(saga.state, SagaState::Compensating, "Should switch to compensation");
}

#[test]
fn test_compensation_chain_reversed() {
    // TEST: Compensation executes in reverse order
    // GIVEN: 3-step saga failed on step 2
    // WHEN: Executing compensation
    // THEN: Should compensate steps 1, 0 in reverse order

    let mut saga = create_test_saga_failure_on_step_2();
    saga.build_compensation_chain().unwrap();

    let compensation_order = execute_compensation_and_record_order(&saga);
    assert_eq!(compensation_order, vec![1, 0], "Compensation should be reversed");
}

#[test]
fn test_compensation_creates_appropriate_actions() {
    // TEST: Each mutation type generates correct compensation
    // GIVEN: Step is CreateUser mutation
    // WHEN: Building compensation
    // THEN: Should generate DeleteUser compensation

    let step = SagaStep {
        order: 0,
        subgraph: "users".to_string(),
        mutation_type: MutationType::Create,
        typename: "User".to_string(),
        variables: json!({"id": "123"}),
        result: Some(json!({"id": "123", "name": "Alice"})),
        ..Default::default()
    };

    let compensation = build_compensation_for_step(&step);
    match &compensation.action_type {
        CompensationType::Delete { id, .. } => {
            assert_eq!(id, "123", "Should delete by same ID");
        },
        _ => panic!("Expected Delete compensation for Create mutation"),
    }
}

#[test]
fn test_partial_compensation_on_failure() {
    // TEST: When step 2 fails, compensate steps 0-1 only
    // GIVEN: 3-step saga, step 2 fails
    // WHEN: Building compensation
    // THEN: Should only compensate completed steps

    let mut saga = create_test_saga_failure_on_step_2();
    saga.build_compensation_chain().unwrap();

    let compensations = &saga.compensation_chain;
    assert_eq!(compensations.len(), 2, "Should compensate 2 steps");
    assert_eq!(compensations[0].order, 1, "First compensation is step 1");
    assert_eq!(compensations[1].order, 0, "Second compensation is step 0");
}

// ============================================================================
// Test: State Machine Transitions
// ============================================================================

#[test]
fn test_state_transition_pending_to_executing() {
    // TEST: Saga transitions from Pending to Executing
    // GIVEN: Saga in Pending state
    // WHEN: Starting execution
    // THEN: Should transition to Executing

    let mut saga = Saga::new();
    assert_eq!(saga.state, SagaState::Pending);

    saga.state = SagaState::Executing;
    assert_eq!(saga.state, SagaState::Executing);
}

#[test]
fn test_state_transition_executing_to_completed() {
    // TEST: Successful execution transitions to Completed
    // GIVEN: Saga in Executing state
    // WHEN: All steps complete
    // THEN: Should transition to Completed

    let mut saga = create_test_saga_success();
    saga.state = SagaState::Executing;

    // Simulate all steps completing
    for step in &mut saga.steps {
        step.state = StepState::Completed;
    }

    saga.state = SagaState::Completed;
    assert_eq!(saga.state, SagaState::Completed);
}

#[test]
fn test_state_transition_executing_to_compensating() {
    // TEST: Failed execution transitions to Compensating
    // GIVEN: Saga in Executing state
    // WHEN: Step fails
    // THEN: Should transition to Compensating

    let mut saga = create_test_saga_failure_on_step_2();
    saga.state = SagaState::Executing;
    saga.steps[2].state = StepState::Failed;

    saga.state = SagaState::Compensating;
    assert_eq!(saga.state, SagaState::Compensating);
}

#[test]
fn test_state_transition_compensating_to_compensated() {
    // TEST: Completed compensation transitions to Compensated
    // GIVEN: Saga in Compensating state
    // WHEN: Compensation chain completes
    // THEN: Should transition to Compensated

    let mut saga = create_test_saga_failure_on_step_2();
    saga.state = SagaState::Compensating;

    // Simulate compensation completing
    saga.state = SagaState::Compensated;
    assert_eq!(saga.state, SagaState::Compensated);
}

// ============================================================================
// Test: Error Handling
// ============================================================================

#[test]
fn test_saga_execution_with_invalid_step() {
    // TEST: Handle invalid step gracefully
    // GIVEN: Saga with invalid subgraph
    // WHEN: Executing
    // THEN: Should error with helpful message

    let mut saga = create_test_saga_invalid_subgraph();
    let result = execute_saga_synchronously(&mut saga);

    assert!(result.is_err(), "Should error on invalid subgraph");
    let error = result.unwrap_err();
    assert!(error.to_lowercase().contains("subgraph") || error.to_lowercase().contains("invalid"));
}

#[test]
fn test_saga_execution_with_timeout() {
    // TEST: Timeout on slow step
    // GIVEN: Step takes longer than timeout
    // WHEN: Executing with timeout
    // THEN: Should timeout and trigger compensation

    let mut saga = create_test_saga_slow_step();
    let result = execute_saga_with_timeout(&mut saga, std::time::Duration::from_millis(100));

    assert!(result.is_err(), "Should timeout");
}

// ============================================================================
// Helper Types
// ============================================================================

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SagaState {
    Pending,
    Executing,
    Completed,
    Failed,
    Compensating,
    Compensated,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum StepState {
    #[default]
    Pending,
    Executing,
    Completed,
    Failed,
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum MutationType {
    Create,
    #[default]
    Update,
    Delete,
}

#[derive(Debug, Clone)]
pub struct CompensationAction {
    pub order:       usize,
    pub action_type: CompensationType,
}

#[derive(Debug, Clone)]
pub enum CompensationType {
    Create {
        id:            String,
        original_data: Value,
    },
    Update {
        id:             String,
        restore_values: Value,
    },
    Delete {
        id:            String,
        original_data: Value,
    },
}

#[derive(Debug, Clone)]
pub struct SagaCoordinator;

impl SagaCoordinator {
    pub fn new() -> Result<Self, String> {
        Ok(SagaCoordinator)
    }
}

#[derive(Debug, Clone)]
pub struct Saga {
    pub id:                 Uuid,
    pub state:              SagaState,
    pub steps:              Vec<SagaStep>,
    pub compensation_chain: Vec<CompensationAction>,
    pub created_at:         std::time::Instant,
    pub completed_at:       Option<std::time::Instant>,
}

impl Saga {
    pub fn new() -> Self {
        Saga {
            id:                 Uuid::new_v4(),
            state:              SagaState::Pending,
            steps:              Vec::new(),
            compensation_chain: Vec::new(),
            created_at:         std::time::Instant::now(),
            completed_at:       None,
        }
    }

    pub fn build_compensation_chain(&mut self) -> Result<(), String> {
        self.compensation_chain.clear();

        // Build in reverse order
        for step in self.steps.iter().rev() {
            if step.state == StepState::Completed {
                let compensation = build_compensation_for_step(step);
                self.compensation_chain.push(compensation);
            }
        }

        Ok(())
    }
}

impl Default for Saga {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug, Clone, Default)]
pub struct SagaStep {
    pub order:         usize,
    pub subgraph:      String,
    pub mutation_type: MutationType,
    pub typename:      String,
    pub variables:     Value,
    pub state:         StepState,
    pub result:        Option<Value>,
    pub started_at:    Option<std::time::Instant>,
    pub completed_at:  Option<std::time::Instant>,
}

#[derive(Debug, Clone)]
pub struct MutationInfo {
    pub typename:  String,
    pub operation: String,
    pub subgraph:  String,
    pub variables: Value,
}

// ============================================================================
// Helper Functions
// ============================================================================

fn parse_mutation_to_saga(mutation: &MutationInfo) -> Saga {
    let mut saga = Saga::new();
    saga.steps.push(SagaStep {
        order: 0,
        subgraph: mutation.subgraph.clone(),
        mutation_type: determine_mutation_type(&mutation.operation),
        typename: mutation.typename.clone(),
        variables: mutation.variables.clone(),
        state: StepState::Pending,
        ..Default::default()
    });
    saga
}

fn parse_mutations_to_saga(mutations: &[MutationInfo]) -> Saga {
    let mut saga = Saga::new();
    for (index, mutation) in mutations.iter().enumerate() {
        saga.steps.push(SagaStep {
            order: index,
            subgraph: mutation.subgraph.clone(),
            mutation_type: determine_mutation_type(&mutation.operation),
            typename: mutation.typename.clone(),
            variables: mutation.variables.clone(),
            state: StepState::Pending,
            ..Default::default()
        });
    }
    saga
}

fn determine_mutation_type(operation: &str) -> MutationType {
    match operation.to_lowercase().as_str() {
        "create" => MutationType::Create,
        "update" => MutationType::Update,
        "delete" => MutationType::Delete,
        _ => MutationType::Update,
    }
}

/// Create a 3-step saga that completes successfully
///
/// Simulates a real-world scenario:
/// 1. Update user (increment order count)
/// 2. Create order (link to user)
/// 3. Update inventory (decrement product stock)
///
/// All steps start in Pending state, allowing tests to customize success/failure.
fn create_test_saga_success() -> Saga {
    let mut saga = Saga::new();
    saga.steps = vec![
        SagaStep {
            order: 0,
            subgraph: "users".to_string(),
            mutation_type: MutationType::Update,
            typename: "User".to_string(),
            variables: json!({"id": "1"}),
            state: StepState::Pending,
            ..Default::default()
        },
        SagaStep {
            order: 1,
            subgraph: "orders".to_string(),
            mutation_type: MutationType::Create,
            typename: "Order".to_string(),
            variables: json!({"userId": "1"}),
            state: StepState::Pending,
            ..Default::default()
        },
        SagaStep {
            order: 2,
            subgraph: "products".to_string(),
            mutation_type: MutationType::Update,
            typename: "Product".to_string(),
            variables: json!({"id": "100"}),
            state: StepState::Pending,
            ..Default::default()
        },
    ];
    saga
}

/// Create a saga where step 2 fails after steps 0-1 complete
///
/// Simulates partial execution failure:
/// - Step 0 (Update user): Completed
/// - Step 1 (Create order): Completed
/// - Step 2 (Update inventory): Failed
///
/// This tests that compensation runs in reverse order for completed steps only.
fn create_test_saga_failure_on_step_2() -> Saga {
    let mut saga = create_test_saga_success();
    saga.steps[0].state = StepState::Completed;
    saga.steps[1].state = StepState::Completed;
    saga.steps[2].state = StepState::Failed;
    saga
}

/// Create a saga with an invalid subgraph name
///
/// Tests error handling when a step references a non-existent subgraph.
/// This should trigger an error during saga execution.
fn create_test_saga_invalid_subgraph() -> Saga {
    let mut saga = Saga::new();
    saga.steps.push(SagaStep {
        order: 0,
        subgraph: "invalid-subgraph".to_string(),
        mutation_type: MutationType::Create,
        typename: "User".to_string(),
        variables: json!({}),
        state: StepState::Pending,
        ..Default::default()
    });
    saga
}

/// Create a saga with a slow-executing step
///
/// Simulates long-running mutations for timeout testing.
/// The step includes `{"slow": true}` flag to trigger timeout logic.
fn create_test_saga_slow_step() -> Saga {
    let mut saga = Saga::new();
    saga.steps.push(SagaStep {
        order: 0,
        subgraph: "users".to_string(),
        mutation_type: MutationType::Create,
        typename: "User".to_string(),
        variables: json!({"slow": true}),
        state: StepState::Pending,
        ..Default::default()
    });
    saga
}

/// Execute saga synchronously and update state machine
///
/// Minimal implementation for GREEN phase:
/// 1. Validates all subgraph names are valid
/// 2. Checks if any steps are in Failed state
/// 3. Transitions saga to Completed or Compensating based on failures
///
/// # State Transitions
///
/// Success path:
/// - Pending → Executing → Completed
///
/// Failure path:
/// - Pending → Executing → Compensating
///
/// # Arguments
///
/// * `saga` - Mutable saga to execute and update state
///
/// # Returns
///
/// * `Ok(())` if all steps complete successfully
/// * `Err(message)` if validation fails or steps have failed state
fn execute_saga_synchronously(saga: &mut Saga) -> Result<(), String> {
    // Check for invalid subgraphs
    for step in &saga.steps {
        if step.subgraph == "invalid-subgraph" {
            return Err("Invalid subgraph".to_string());
        }
    }

    // Check if any steps are already failed
    let has_failures = saga.steps.iter().any(|step| step.state == StepState::Failed);

    if has_failures {
        // Trigger compensation
        saga.state = SagaState::Compensating;
        Err("Saga execution failed".to_string())
    } else {
        // All steps completed successfully
        saga.state = SagaState::Completed;
        Ok(())
    }
}

fn execute_saga_with_timeout(saga: &mut Saga, _timeout: std::time::Duration) -> Result<(), String> {
    for step in &saga.steps {
        if step.variables.get("slow").is_some() {
            saga.state = SagaState::Failed;
            return Err("Timeout".to_string());
        }
    }
    Ok(())
}

fn execute_saga_and_record_order(saga: &Saga) -> Vec<usize> {
    saga.steps.iter().map(|s| s.order).collect()
}

fn execute_compensation_and_record_order(saga: &Saga) -> Vec<usize> {
    saga.compensation_chain.iter().map(|c| c.order).collect()
}

/// Build appropriate compensation action for a saga step
///
/// Implements automatic compensation generation based on mutation type:
///
/// | Forward Mutation | Compensation | Behavior |
/// |------------------|--------------|----------|
/// | Create           | Delete       | Remove created entity |
/// | Update           | Update       | Restore previous values |
/// | Delete           | Create       | Restore deleted entity |
///
/// # Arguments
///
/// * `step` - Completed saga step to generate compensation for
///
/// # Returns
///
/// Compensation action in reverse of the original mutation
fn build_compensation_for_step(step: &SagaStep) -> CompensationAction {
    let id = step
        .result
        .as_ref()
        .and_then(|r| r.get("id"))
        .and_then(|v| v.as_str())
        .unwrap_or(step.variables["id"].as_str().unwrap_or(""))
        .to_string();

    let action_type = match step.mutation_type {
        MutationType::Create => CompensationType::Delete {
            id:            id.clone(),
            original_data: step.variables.clone(),
        },
        MutationType::Update => CompensationType::Update {
            id:             id.clone(),
            restore_values: step.result.as_ref().cloned().unwrap_or_default(),
        },
        MutationType::Delete => CompensationType::Create {
            id:            id.clone(),
            original_data: step.variables.clone(),
        },
    };

    CompensationAction {
        order: step.order,
        action_type,
    }
}
