//! Comprehensive integration tests for ALL generation endpoints
//! Tests with REAL data against REAL Suno API endpoints

use std::time::Duration;
use dotenvy::dotenv;
use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
use sunocore::models::generation::{GenerateParams, GenerationResponse};
use sunocore::models::feed::FeedResponse;
use sunocore::validation::env::{suno_api_key_unwrap, suno_base_url_unwrap};

fn create_client(api_key: &str) -> Client {
    let mut headers = HeaderMap::new();
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
    headers.insert(
        AUTHORIZATION,
        HeaderValue::from_str(&format!("Bearer {}", api_key)).expect("valid header"),
    );
    headers.insert("X-Api-Key", HeaderValue::from_str(api_key).expect("valid header"));

    Client::builder()
        .default_headers(headers)
        .timeout(Duration::from_secs(60))
        .build()
        .expect("build http client")
}

#[test]
fn test_submit_basic_generation_real_api() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = create_client(&api_key);
    let url = format!("{}/api/generate/v2", base.trim_end_matches('/'));

    let params = GenerateParams::new("a short happy melody");
    let resp = client.post(&url).json(&params).send().unwrap();

    assert!(resp.status().is_success(), "Generation should succeed: {}", resp.status());

    let body = resp.text().unwrap();
    let gen_response: GenerationResponse = serde_json::from_str(&body)
        .unwrap_or_else(|e| panic!("Deserialization failed: {}\nBody: {}", e, &body[..body.len().min(500)]));

    println!("✓ Basic generation: {} clips", gen_response.clips.len());
    assert!(!gen_response.clips.is_empty(), "Should return at least one clip");
}

#[test]
fn test_continue_generation_real_api() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = create_client(&api_key);

    // First generate a clip to continue from
    let url = format!("{}/api/generate/v2", base.trim_end_matches('/'));
    let params = GenerateParams::new("original melody");
    let resp = client.post(&url).json(&params).send().unwrap();
    assert!(resp.status().is_success());

    let gen_response: GenerationResponse = resp.json().unwrap();
    let clip_id = gen_response.clips[0].id.clone();

    println!("Generated clip to continue: {}", clip_id);

    // Wait a bit for the clip to be ready
    std::thread::sleep(Duration::from_secs(5));

    // Now continue from it
    let continue_params = GenerateParams::new("continue with different style")
        .with_continue(&clip_id)
        .with_task("extend");

    let resp = client.post(&url).json(&continue_params).send().unwrap();

    assert!(resp.status().is_success(), "Continue should succeed: {}", resp.status());

    let body = resp.text().unwrap();
    let continue_response: GenerationResponse = serde_json::from_str(&body)
        .unwrap_or_else(|e| panic!("Deserialization failed: {}\nBody: {}", e, &body[..body.len().min(500)]));

    println!("✓ Continue generation: {} clips", continue_response.clips.len());
    assert!(!continue_response.clips.is_empty());

    // Verify the metadata shows it's a continuation
    if let Some(ref metadata) = continue_response.clips[0].metadata {
        println!("  Task: {:?}", metadata.task);
        assert_eq!(metadata.task, Some("extend".to_string()));
    }
}

#[test]
fn test_infill_generation_real_api() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = create_client(&api_key);

    // First generate a clip to infill
    let url = format!("{}/api/generate/v2", base.trim_end_matches('/'));
    let params = GenerateParams::new("base track for infill");
    let resp = client.post(&url).json(&params).send().unwrap();
    assert!(resp.status().is_success());

    let gen_response: GenerationResponse = resp.json().unwrap();
    let clip_id = gen_response.clips[0].id.clone();

    println!("Generated clip to infill: {}", clip_id);

    // Wait for it to process
    std::thread::sleep(Duration::from_secs(5));

    // Now infill it
    let infill_params = GenerateParams::new("add drums here")
        .with_continue(&clip_id)
        .with_infill(5.0, 10.0)
        .with_task("infill");

    let resp = client.post(&url).json(&infill_params).send().unwrap();

    assert!(resp.status().is_success(), "Infill should succeed: {}", resp.status());

    let body = resp.text().unwrap();
    let infill_response: GenerationResponse = serde_json::from_str(&body)
        .unwrap_or_else(|e| panic!("Deserialization failed: {}\nBody: {}", e, &body[..body.len().min(500)]));

    println!("✓ Infill generation: {} clips", infill_response.clips.len());
    assert!(!infill_response.clips.is_empty());

    // Verify the metadata shows it's an infill
    if let Some(ref metadata) = infill_response.clips[0].metadata {
        println!("  Task: {:?}", metadata.task);
        assert_eq!(metadata.task, Some("infill".to_string()));
    }
}

#[test]
fn test_poll_with_feed_v2_ids_real_api() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = create_client(&api_key);

    // Generate a clip
    let gen_url = format!("{}/api/generate/v2", base.trim_end_matches('/'));
    let params = GenerateParams::new("poll test");
    let resp = client.post(&gen_url).json(&params).send().unwrap();
    assert!(resp.status().is_success());

    let gen_response: GenerationResponse = resp.json().unwrap();
    let clip_ids: Vec<String> = gen_response.clips.iter().map(|c| c.id.clone()).collect();

    println!("Testing poll for {} clips", clip_ids.len());

    // Poll using GET /api/feed/v2?ids=
    let ids_param = clip_ids.join(",");
    let poll_url = format!("{}/api/feed/v2?ids={}", base.trim_end_matches('/'), ids_param);

    // Poll multiple times to observe status progression
    for attempt in 1..=10 {
        std::thread::sleep(Duration::from_secs(2));

        let resp = client.get(&poll_url).send().unwrap();
        assert!(resp.status().is_success(), "Poll should succeed: {}", resp.status());

        let body = resp.text().unwrap();
        let feed_response: FeedResponse = serde_json::from_str(&body)
            .unwrap_or_else(|e| panic!("Deserialization failed: {}\nBody: {}", e, &body[..body.len().min(500)]));

        println!("Attempt {}: {} clips returned", attempt, feed_response.clips.len());

        let mut all_have_cdn = true;
        for clip in &feed_response.clips {
            let has_cdn = clip.audio_url.contains("cdn") && !clip.audio_url.is_empty();
            println!("  Clip {}: status='{}', has_cdn={}", &clip.id.to_string()[..8], clip.status, has_cdn);

            if !has_cdn {
                all_have_cdn = false;
            }
        }

        if all_have_cdn {
            println!("✓ All clips have CDN URLs");
            return;
        }
    }

    println!("! Clips may need more time (normal for real generation)");
}

#[test]
fn test_generation_with_all_params_real_api() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = create_client(&api_key);
    let url = format!("{}/api/generate/v2", base.trim_end_matches('/'));

    // Test with all available parameters (skip tags if they cause issues)
    let params = GenerateParams::new("a jazzy instrumental")
        .with_title("Jazz Test")
        .with_instrumental(true)
        .with_model("chirp-v2");

    let resp = client.post(&url).json(&params).send().unwrap();

    assert!(resp.status().is_success(), "Full params generation should succeed: {}", resp.status());

    let body = resp.text().unwrap();
    let gen_response: GenerationResponse = serde_json::from_str(&body)
        .unwrap_or_else(|e| panic!("Deserialization failed: {}\nBody: {}", e, &body[..body.len().min(500)]));

    println!("✓ Generation with all params: {} clips", gen_response.clips.len());

    // Verify parameters were applied
    if let Some(ref clip) = gen_response.clips.first() {
        if let Some(ref title) = clip.title {
            assert_eq!(title, "Jazz Test");
            println!("  Title: {}", title);
        }
        if let Some(ref metadata) = clip.metadata {
            println!("  Prompt: {}", metadata.prompt);
            assert_eq!(metadata.prompt, "a jazzy instrumental");
        }
    }
}

#[test]
fn test_batch_poll_multiple_clips_real_api() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = create_client(&api_key);

    // Generate multiple clips
    let gen_url = format!("{}/api/generate/v2", base.trim_end_matches('/'));
    let params = GenerateParams::new("batch test");
    let resp = client.post(&gen_url).json(&params).send().unwrap();
    assert!(resp.status().is_success());

    let gen_response: GenerationResponse = resp.json().unwrap();
    let clip_ids: Vec<String> = gen_response.clips.iter().map(|c| c.id.clone()).collect();

    println!("Testing batch poll for {} clips", clip_ids.len());
    assert!(clip_ids.len() >= 2, "Should generate at least 2 clips for batch test");

    // Batch poll all clips at once
    let ids_param = clip_ids.join(",");
    let poll_url = format!("{}/api/feed/v2?ids={}", base.trim_end_matches('/'), ids_param);

    let resp = client.get(&poll_url).send().unwrap();
    assert!(resp.status().is_success(), "Batch poll should succeed");

    let feed_response: FeedResponse = resp.json().unwrap();

    println!("✓ Batch poll returned {} clips", feed_response.clips.len());
    assert_eq!(feed_response.clips.len(), clip_ids.len(), "Should return all requested clips");

    // Verify each clip was returned
    for clip_id in &clip_ids {
        let found = feed_response.clips.iter().any(|c| c.id.to_string() == *clip_id);
        assert!(found, "Clip {} should be in response", clip_id);
    }
}
