//! Integration tests for queue stats repository
//!
//! Tests queue statistics persistence and retrieval operations.

use attune_common::repositories::queue_stats::{QueueStatsRepository, UpsertQueueStatsInput};
use chrono::Utc;

mod helpers;
use helpers::{ActionFixture, PackFixture};

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_upsert_queue_stats() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack and action using fixtures
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();
    let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "test_action")
        .create(&pool)
        .await
        .unwrap();

    // Upsert queue stats (insert)
    let input = UpsertQueueStatsInput {
        action_id: action.id,
        queue_length: 5,
        active_count: 2,
        max_concurrent: 3,
        oldest_enqueued_at: Some(Utc::now()),
        total_enqueued: 100,
        total_completed: 95,
    };

    let stats = QueueStatsRepository::upsert(&pool, input.clone())
        .await
        .unwrap();

    assert_eq!(stats.action_id, action.id);
    assert_eq!(stats.queue_length, 5);
    assert_eq!(stats.active_count, 2);
    assert_eq!(stats.max_concurrent, 3);
    assert_eq!(stats.total_enqueued, 100);
    assert_eq!(stats.total_completed, 95);
    assert!(stats.oldest_enqueued_at.is_some());

    // Upsert again (update)
    let update_input = UpsertQueueStatsInput {
        action_id: action.id,
        queue_length: 3,
        active_count: 3,
        max_concurrent: 3,
        oldest_enqueued_at: None,
        total_enqueued: 110,
        total_completed: 107,
    };

    let updated_stats = QueueStatsRepository::upsert(&pool, update_input)
        .await
        .unwrap();

    assert_eq!(updated_stats.action_id, action.id);
    assert_eq!(updated_stats.queue_length, 3);
    assert_eq!(updated_stats.active_count, 3);
    assert_eq!(updated_stats.total_enqueued, 110);
    assert_eq!(updated_stats.total_completed, 107);
    assert!(updated_stats.oldest_enqueued_at.is_none());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_queue_stats_by_action() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack and action
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();
    let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "test_action")
        .create(&pool)
        .await
        .unwrap();

    // No stats initially
    let result = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap();
    assert!(result.is_none());

    // Create stats
    let input = UpsertQueueStatsInput {
        action_id: action.id,
        queue_length: 10,
        active_count: 5,
        max_concurrent: 5,
        oldest_enqueued_at: Some(Utc::now()),
        total_enqueued: 200,
        total_completed: 190,
    };

    QueueStatsRepository::upsert(&pool, input).await.unwrap();

    // Find stats
    let stats = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap()
        .expect("Stats should exist");

    assert_eq!(stats.action_id, action.id);
    assert_eq!(stats.queue_length, 10);
    assert_eq!(stats.active_count, 5);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_list_active_queue_stats() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();

    // Create multiple actions with different queue states
    for i in 0..3 {
        let action = ActionFixture::new_unique(pack.id, &pack.r#ref, &format!("action_{}", i))
            .create(&pool)
            .await
            .unwrap();

        let input = if i == 0 {
            // Active queue
            UpsertQueueStatsInput {
                action_id: action.id,
                queue_length: 5,
                active_count: 2,
                max_concurrent: 3,
                oldest_enqueued_at: Some(Utc::now()),
                total_enqueued: 50,
                total_completed: 45,
            }
        } else if i == 1 {
            // Active executions but no queue
            UpsertQueueStatsInput {
                action_id: action.id,
                queue_length: 0,
                active_count: 3,
                max_concurrent: 3,
                oldest_enqueued_at: None,
                total_enqueued: 30,
                total_completed: 27,
            }
        } else {
            // Idle (should not appear in active list)
            UpsertQueueStatsInput {
                action_id: action.id,
                queue_length: 0,
                active_count: 0,
                max_concurrent: 3,
                oldest_enqueued_at: None,
                total_enqueued: 20,
                total_completed: 20,
            }
        };

        QueueStatsRepository::upsert(&pool, input).await.unwrap();
    }

    // List active queues
    let active_stats = QueueStatsRepository::list_active(&pool).await.unwrap();

    // Should only return entries with queue_length > 0 or active_count > 0
    // At least 2 from our test data (may be more from other tests)
    let our_active = active_stats
        .iter()
        .filter(|s| s.queue_length > 0 || s.active_count > 0)
        .count();
    assert!(our_active >= 2);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_delete_queue_stats() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack and action
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();
    let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "test_action")
        .create(&pool)
        .await
        .unwrap();

    // Create stats
    let input = UpsertQueueStatsInput {
        action_id: action.id,
        queue_length: 5,
        active_count: 2,
        max_concurrent: 3,
        oldest_enqueued_at: Some(Utc::now()),
        total_enqueued: 100,
        total_completed: 95,
    };

    QueueStatsRepository::upsert(&pool, input).await.unwrap();

    // Verify exists
    let stats = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap();
    assert!(stats.is_some());

    // Delete
    let deleted = QueueStatsRepository::delete(&pool, action.id)
        .await
        .unwrap();
    assert!(deleted);

    // Verify deleted
    let stats = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap();
    assert!(stats.is_none());

    // Delete again (should return false)
    let deleted = QueueStatsRepository::delete(&pool, action.id)
        .await
        .unwrap();
    assert!(!deleted);
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_batch_upsert_queue_stats() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();

    // Create multiple actions
    let mut inputs = Vec::new();
    for i in 0..5 {
        let action = ActionFixture::new_unique(pack.id, &pack.r#ref, &format!("action_{}", i))
            .create(&pool)
            .await
            .unwrap();

        inputs.push(UpsertQueueStatsInput {
            action_id: action.id,
            queue_length: i,
            active_count: i,
            max_concurrent: 5,
            oldest_enqueued_at: if i > 0 { Some(Utc::now()) } else { None },
            total_enqueued: (i * 10) as i64,
            total_completed: (i * 9) as i64,
        });
    }

    // Batch upsert
    let results = QueueStatsRepository::batch_upsert(&pool, inputs)
        .await
        .unwrap();

    assert_eq!(results.len(), 5);

    // Verify each result
    for (i, stats) in results.iter().enumerate() {
        assert_eq!(stats.queue_length, i as i32);
        assert_eq!(stats.active_count, i as i32);
        assert_eq!(stats.total_enqueued, (i * 10) as i64);
        assert_eq!(stats.total_completed, (i * 9) as i64);
    }
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_clear_stale_queue_stats() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();

    // Create action with idle stats
    let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "test_action")
        .create(&pool)
        .await
        .unwrap();

    // Create idle stats (queue_length = 0, active_count = 0)
    let input = UpsertQueueStatsInput {
        action_id: action.id,
        queue_length: 0,
        active_count: 0,
        max_concurrent: 3,
        oldest_enqueued_at: None,
        total_enqueued: 100,
        total_completed: 100,
    };

    QueueStatsRepository::upsert(&pool, input).await.unwrap();

    // Try to clear stale stats (with very large timeout - should not delete recent stats)
    let _cleared = QueueStatsRepository::clear_stale(&pool, 3600)
        .await
        .unwrap();
    // May or may not be 0 depending on other test data, but our stat should still exist

    // Verify our stat still exists (was just created)
    let stats = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap();
    assert!(stats.is_some());
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_queue_stats_cascade_delete() {
    let pool = helpers::create_test_pool().await.unwrap();

    // Create test pack and action
    let pack = PackFixture::new_unique("test").create(&pool).await.unwrap();
    let action = ActionFixture::new_unique(pack.id, &pack.r#ref, "test_action")
        .create(&pool)
        .await
        .unwrap();

    // Create stats
    let input = UpsertQueueStatsInput {
        action_id: action.id,
        queue_length: 5,
        active_count: 2,
        max_concurrent: 3,
        oldest_enqueued_at: Some(Utc::now()),
        total_enqueued: 100,
        total_completed: 95,
    };

    QueueStatsRepository::upsert(&pool, input).await.unwrap();

    // Verify stats exist
    let stats = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap();
    assert!(stats.is_some());

    // Delete the action (should cascade to queue_stats)
    use attune_common::repositories::action::ActionRepository;
    use attune_common::repositories::Delete;
    ActionRepository::delete(&pool, action.id).await.unwrap();

    // Verify stats are also deleted (cascade)
    let stats = QueueStatsRepository::find_by_action(&pool, action.id)
        .await
        .unwrap();
    assert!(stats.is_none());
}
