//! Integration tests for webhook functionality

use attune_common::models::trigger::Trigger;
use attune_common::repositories::trigger::{CreateTriggerInput, TriggerRepository};
use attune_common::repositories::{Create, FindById};
use sqlx::postgres::PgPoolOptions;
use sqlx::PgPool;

async fn setup_test_db() -> PgPool {
    let database_url = std::env::var("DATABASE_URL")
        .unwrap_or_else(|_| "postgresql://postgres:postgres@localhost:5432/attune".to_string());

    PgPoolOptions::new()
        .max_connections(5)
        .connect(&database_url)
        .await
        .expect("Failed to create database pool")
}

async fn create_test_trigger(pool: &PgPool) -> Trigger {
    let input = CreateTriggerInput {
        r#ref: format!("test.webhook_trigger_{}", uuid::Uuid::new_v4()),
        pack: None,
        pack_ref: Some("test".to_string()),
        label: "Test Webhook Trigger".to_string(),
        description: Some("A test trigger for webhook functionality".to_string()),
        enabled: true,
        param_schema: None,
        out_schema: None,
        is_adhoc: false,
    };

    TriggerRepository::create(pool, input)
        .await
        .expect("Failed to create test trigger")
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_enable() {
    let pool = setup_test_db().await;
    let trigger = create_test_trigger(&pool).await;

    // Initially, webhook should be disabled
    assert!(!trigger.webhook_enabled);
    assert!(trigger.webhook_key.is_none());

    // Enable webhooks
    let webhook_info = TriggerRepository::enable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to enable webhook");

    // Verify webhook info
    assert!(webhook_info.enabled);
    assert!(webhook_info.webhook_key.starts_with("wh_"));
    assert_eq!(webhook_info.webhook_key.len(), 35); // "wh_" + 32 chars
    assert!(webhook_info.webhook_url.contains(&webhook_info.webhook_key));

    // Fetch trigger again to verify database state
    let updated_trigger = TriggerRepository::find_by_id(&pool, trigger.id)
        .await
        .expect("Failed to fetch trigger")
        .expect("Trigger not found");

    assert!(updated_trigger.webhook_enabled);
    assert_eq!(
        updated_trigger.webhook_key.as_ref().unwrap(),
        &webhook_info.webhook_key
    );

    // Cleanup
    sqlx::query("DELETE FROM attune.trigger WHERE id = $1")
        .bind(trigger.id)
        .execute(&pool)
        .await
        .expect("Failed to cleanup");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_disable() {
    let pool = setup_test_db().await;
    let trigger = create_test_trigger(&pool).await;

    // Enable webhooks first
    let webhook_info = TriggerRepository::enable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to enable webhook");

    let webhook_key = webhook_info.webhook_key.clone();

    // Disable webhooks
    let result = TriggerRepository::disable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to disable webhook");

    assert!(result);

    // Fetch trigger to verify
    let updated_trigger = TriggerRepository::find_by_id(&pool, trigger.id)
        .await
        .expect("Failed to fetch trigger")
        .expect("Trigger not found");

    assert!(!updated_trigger.webhook_enabled);
    // Key should still be present (for audit purposes)
    assert_eq!(updated_trigger.webhook_key.as_ref().unwrap(), &webhook_key);

    // Cleanup
    sqlx::query("DELETE FROM attune.trigger WHERE id = $1")
        .bind(trigger.id)
        .execute(&pool)
        .await
        .expect("Failed to cleanup");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_key_regeneration() {
    let pool = setup_test_db().await;
    let trigger = create_test_trigger(&pool).await;

    // Enable webhooks
    let initial_info = TriggerRepository::enable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to enable webhook");

    let old_key = initial_info.webhook_key.clone();

    // Regenerate key
    let regenerate_result = TriggerRepository::regenerate_webhook_key(&pool, trigger.id)
        .await
        .expect("Failed to regenerate webhook key");

    assert!(regenerate_result.previous_key_revoked);
    assert_ne!(regenerate_result.webhook_key, old_key);
    assert!(regenerate_result.webhook_key.starts_with("wh_"));

    // Fetch trigger to verify new key
    let updated_trigger = TriggerRepository::find_by_id(&pool, trigger.id)
        .await
        .expect("Failed to fetch trigger")
        .expect("Trigger not found");

    assert_eq!(
        updated_trigger.webhook_key.as_ref().unwrap(),
        &regenerate_result.webhook_key
    );

    // Cleanup
    sqlx::query("DELETE FROM attune.trigger WHERE id = $1")
        .bind(trigger.id)
        .execute(&pool)
        .await
        .expect("Failed to cleanup");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_find_by_webhook_key() {
    let pool = setup_test_db().await;
    let trigger = create_test_trigger(&pool).await;

    // Enable webhooks
    let webhook_info = TriggerRepository::enable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to enable webhook");

    // Find by webhook key
    let found_trigger = TriggerRepository::find_by_webhook_key(&pool, &webhook_info.webhook_key)
        .await
        .expect("Failed to find trigger by webhook key")
        .expect("Trigger not found");

    assert_eq!(found_trigger.id, trigger.id);
    assert_eq!(found_trigger.r#ref, trigger.r#ref);
    assert!(found_trigger.webhook_enabled);

    // Test with invalid key
    let not_found =
        TriggerRepository::find_by_webhook_key(&pool, "wh_invalid_key_12345678901234567890")
            .await
            .expect("Query failed");

    assert!(not_found.is_none());

    // Cleanup
    sqlx::query("DELETE FROM attune.trigger WHERE id = $1")
        .bind(trigger.id)
        .execute(&pool)
        .await
        .expect("Failed to cleanup");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_webhook_key_uniqueness() {
    let pool = setup_test_db().await;
    let trigger1 = create_test_trigger(&pool).await;
    let trigger2 = create_test_trigger(&pool).await;

    // Enable webhooks for both triggers
    let info1 = TriggerRepository::enable_webhook(&pool, trigger1.id)
        .await
        .expect("Failed to enable webhook for trigger 1");

    let info2 = TriggerRepository::enable_webhook(&pool, trigger2.id)
        .await
        .expect("Failed to enable webhook for trigger 2");

    // Keys should be different
    assert_ne!(info1.webhook_key, info2.webhook_key);

    // Both should be valid format
    assert!(info1.webhook_key.starts_with("wh_"));
    assert!(info2.webhook_key.starts_with("wh_"));

    // Cleanup
    sqlx::query("DELETE FROM attune.trigger WHERE id IN ($1, $2)")
        .bind(trigger1.id)
        .bind(trigger2.id)
        .execute(&pool)
        .await
        .expect("Failed to cleanup");
}

#[tokio::test]
#[ignore = "integration test — requires database"]
async fn test_enable_webhook_idempotent() {
    let pool = setup_test_db().await;
    let trigger = create_test_trigger(&pool).await;

    // Enable webhooks first time
    let info1 = TriggerRepository::enable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to enable webhook");

    // Enable webhooks second time (should return same key)
    let info2 = TriggerRepository::enable_webhook(&pool, trigger.id)
        .await
        .expect("Failed to enable webhook again");

    // Should return the same key
    assert_eq!(info1.webhook_key, info2.webhook_key);
    assert!(info2.enabled);

    // Cleanup
    sqlx::query("DELETE FROM attune.trigger WHERE id = $1")
        .bind(trigger.id)
        .execute(&pool)
        .await
        .expect("Failed to cleanup");
}
