//! Integration tests for data download workflow
//!
//! These tests validate the core download workflow:
//! 1. Schedule download request
//! 2. Download data from Databento (mocked)
//! 3. Validate data quality
//! 4. Upload to MinIO
//! 5. Update job status
//!
//! TDD: These tests are written FIRST and should FAIL until implementation is complete.

#[path = "common/workflow_types.rs"]
mod workflow_types;
#[path = "common/mock_service.rs"]
mod mock_service;

use mock_service::{create_test_service, create_test_service_with_corrupted_data};
use workflow_types::ScheduleDownloadRequest;
use tempfile::TempDir;

// Import proto enum for DownloadStatus
use data_acquisition_service::proto::DownloadStatus;

// Helper function to create test download request
fn create_test_download_request() -> ScheduleDownloadRequest {
    ScheduleDownloadRequest::new_test_request()
}

/// Test: Schedule a download and verify it's created with PENDING status
#[tokio::test]
async fn test_schedule_download_creates_pending_job() {
    // Arrange: Create test service instance
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service(temp_dir.path()).await;
    let request = create_test_download_request();

    // Act: Schedule download
    let response = service
        .schedule_download(request)
        .await
        .expect("Schedule download failed");

    // Assert: Job created with PENDING status
    assert!(!response.job_id.is_empty(), "Job ID should not be empty");
    assert_eq!(
        response.status,
        DownloadStatus::Pending as i32,
        "Initial status should be PENDING"
    );
    assert!(
        response.estimated_cost_usd > 0.0,
        "Should have estimated cost"
    );
}

/// Test: Download workflow progresses through all states
#[tokio::test]
async fn test_download_workflow_progresses_through_states() {
    // Arrange
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service(temp_dir.path()).await;
    let request = create_test_download_request();

    // Act: Schedule download
    let schedule_response = service
        .schedule_download(request)
        .await
        .expect("Schedule failed");
    let job_id = schedule_response.job_id.clone();

    // Wait for download to start
    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

    // Assert: Job should progress to DOWNLOADING
    let status1 = service
        .get_download_status(job_id.clone())
        .await
        .expect("Get status failed");
    assert!(
        status1.job_details.status == DownloadStatus::Downloading as i32
            || status1.job_details.status == DownloadStatus::Validating as i32,
        "Job should be downloading or validating, got: {}",
        status1.job_details.status
    );

    // Wait for completion (with timeout)
    for _ in 0..100 {
        let status = service
            .get_download_status(job_id.clone())
            .await
            .expect("Get status failed");

        if status.job_details.status == DownloadStatus::Completed as i32 {
            // Assert: Final state should be COMPLETED
            assert_eq!(status.job_details.progress_percentage, 100.0);
            assert!(status.job_details.completed_at > 0);
            assert!(!status.job_details.minio_path.is_empty());
            assert!(status.job_details.records_count > 0);
            return; // Success!
        }

        tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
    }

    panic!("Download did not complete within timeout");
}

/// Test: Get download status returns accurate progress
#[tokio::test]
async fn test_get_download_status_returns_accurate_progress() {
    // Arrange
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service(temp_dir.path()).await;
    let request = create_test_download_request();

    // Act: Schedule download
    let schedule_response = service
        .schedule_download(request)
        .await
        .expect("Schedule failed");

    let status_response = service
        .get_download_status(schedule_response.job_id.clone())
        .await
        .expect("Get status failed");

    // Assert: Status response has valid fields
    assert_eq!(status_response.job_details.job_id, schedule_response.job_id);
    assert_eq!(status_response.job_details.dataset, "GLBX.MDP3");
    assert_eq!(status_response.job_details.symbols.len(), 2);
    assert!(status_response.job_details.progress_percentage >= 0.0);
    assert!(status_response.job_details.progress_percentage <= 100.0);
}

/// Test: List download jobs with pagination
#[tokio::test]
async fn test_list_download_jobs_with_pagination() {
    // Arrange: Create multiple download jobs
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service(temp_dir.path()).await;

    let mut job_ids = vec![];
    for i in 0..5 {
        let mut request = create_test_download_request();
        request.description = format!("Test download {}", i);
        let response = service
            .schedule_download(request)
            .await
            .expect("Schedule failed");
        job_ids.push(response.job_id);
    }

    // Act: List with pagination
    let list_response = service
        .list_download_jobs(1, 3, None, None, None)
        .await
        .expect("List failed");

    // Assert: Pagination works correctly
    assert_eq!(list_response.jobs.len(), 3, "Should return 3 jobs per page");
    assert_eq!(list_response.total_count, 5, "Should have 5 total jobs");
    assert_eq!(list_response.page, 1);
    assert_eq!(list_response.page_size, 3);
}

/// Test: Cancel download job
#[tokio::test]
async fn test_cancel_download_job() {
    // Arrange
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service(temp_dir.path()).await;
    let request = create_test_download_request();

    let schedule_response = service
        .schedule_download(request)
        .await
        .expect("Schedule failed");
    let job_id = schedule_response.job_id.clone();

    // Act: Cancel job
    let cancel_response = service
        .cancel_download(job_id.clone(), "Test cancellation".to_string())
        .await
        .expect("Cancel failed");

    // Assert: Job is cancelled
    assert!(cancel_response.success, "Cancel should succeed");

    let status = service
        .get_download_status(job_id)
        .await
        .expect("Get status failed");
    assert_eq!(status.job_details.status, DownloadStatus::Cancelled as i32);
}

/// Test: Data quality validation detects issues
#[tokio::test]
async fn test_data_quality_validation_detects_issues() {
    // Arrange: Service with mock corrupted data
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service_with_corrupted_data(temp_dir.path()).await;
    let request = create_test_download_request();

    // Act: Schedule download (will get corrupted data)
    let schedule_response = service
        .schedule_download(request)
        .await
        .expect("Schedule failed");

    // Wait for completion
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;

    // Assert: Quality score should be low
    let status = service
        .get_download_status(schedule_response.job_id)
        .await
        .expect("Get status failed");

    assert!(
        status.job_details.data_quality_score < 0.95,
        "Quality score should be low for corrupted data"
    );
    assert!(status.job_details.invalid_records > 0);
}

/// Test: Cost estimation is accurate
#[tokio::test]
async fn test_cost_estimation_is_accurate() {
    // Arrange
    let temp_dir = TempDir::new().expect("Failed to create temp dir");
    let service = create_test_service(temp_dir.path()).await;

    // Test different date ranges
    let test_cases = vec![
        (1, 0.0, 2.0),    // 1 day: $0-2
        (7, 5.0, 15.0),   // 7 days: $5-15
        (30, 20.0, 60.0), // 30 days: $20-60
    ];

    for (days, min_cost, max_cost) in test_cases {
        let mut request = create_test_download_request();
        request.start_date = "2024-01-01".to_string();
        request.end_date = format!("2024-01-{:02}", days + 1);

        // Act
        let response = service
            .schedule_download(request)
            .await
            .expect("Schedule failed");

        // Assert: Cost is in expected range
        assert!(
            response.estimated_cost_usd >= min_cost,
            "Cost {} should be >= {}",
            response.estimated_cost_usd,
            min_cost
        );
        assert!(
            response.estimated_cost_usd <= max_cost,
            "Cost {} should be <= {}",
            response.estimated_cost_usd,
            max_cost
        );
    }
}
