use std::time::Duration;

use dotenvy::dotenv;
use reqwest::blocking::Client;
use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue};
use sunocore::models::generation::{GenerateParams, GenerationStatus, ClipStatus};
use sunocore::models::audio_download::AudioFormat;
use sunocore::validation::env::{suno_api_key_unwrap, suno_base_url_unwrap};

/// Helper to create an HTTP client with auth headers
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: Submit a generation request
/// POST /api/generate/v2
#[test]
fn test_submit_generation() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY not set");

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

    let params = GenerateParams::new("a short upbeat electronic song")
        .with_title("Test Generation");

    let resp = client
        .post(&url)
        .json(&params)
        .send()
        .unwrap_or_else(|e| panic!("request to {} failed: {}", url, e));

    println!("Generation submit response: {}", resp.status());

    assert!(
        resp.status().is_success() || resp.status().is_client_error(),
        "HTTP {} submitting generation to {}",
        resp.status(),
        url
    );

    if resp.status().is_success() {
        let body = resp
            .text()
            .unwrap_or_else(|e| panic!("reading body failed: {}", e));

        let gen_response: sunocore::models::generation::GenerationResponse =
            serde_json::from_str(&body).unwrap_or_else(|e| {
                panic!(
                    "deserialization failed: {}\nbody: {}",
                    e,
                    &body[..body.len().min(2000)]
                )
            });

        println!("Generation ID: {}", gen_response.id);
        println!("Status: {}", gen_response.status);
        println!("Clips count: {}", gen_response.clips.len());

        // Status can be pending, processing, or complete at submit time
        assert!(
            matches!(gen_response.status, GenerationStatus::Pending | GenerationStatus::Processing | GenerationStatus::Complete),
            "Initial status should be Pending, Processing, or Complete, got: {}",
            gen_response.status
        );

        if !gen_response.clips.is_empty() {
            let first_clip = &gen_response.clips[0];
            println!("First clip ID: {}", first_clip.id);
            println!("First clip status: {}", first_clip.status);

            // Poll status a few times to see if it progresses
            test_poll_generation(&base, &api_key, &first_clip.id);
        }
    }
}

/// Test: Poll a generation status
/// GET /api/clips/{id}
fn test_poll_generation(base_url: &str, api_key: &str, clip_id: &str) {
    let url = format!("{}/api/clips/{}", base_url.trim_end_matches('/'), clip_id);
    let client = create_client(api_key);

    println!("\nPolling clip: {}", clip_id);

    // Poll up to 3 times to check status progression
    for attempt in 1..=3 {
        let resp = client
            .get(&url)
            .send()
            .unwrap_or_else(|e| panic!("poll request failed: {}", e));

        if !resp.status().is_success() {
            println!(
                "Poll failed with status {}, endpoint might not be available yet",
                resp.status()
            );
            // Clip might not be visible yet, or endpoint might be different
            return;
        }

        let body = resp
            .text()
            .unwrap_or_else(|e| panic!("reading body failed: {}", e));

        let clip: sunocore::models::generation::GeneratedClip =
            serde_json::from_str(&body).unwrap_or_else(|e| {
                panic!(
                    "deserialization failed: {}\nbody: {}",
                    e,
                    &body[..body.len().min(2000)]
                )
            });

        println!("Attempt {}: Status = {}", attempt, clip.status);

        if let Some(ref url) = clip.audio_url {
            println!("  Audio URL: {}", &url[..url.len().min(80)]);
        }

        match clip.status {
            ClipStatus::Complete => {
                println!("Generation completed!");
                assert!(clip.audio_url.is_some(), "Complete clip should have audio_url");
                break;
            }
            ClipStatus::Error => {
                println!("Generation error: {:?}", clip.error_message());
                break;
            }
            ClipStatus::Submitted | ClipStatus::Pending => {
                // Still in progress, continue polling
                println!("  Still in progress...");
            }
        }

        // Small delay between polls
        std::thread::sleep(Duration::from_secs(2));
    }
}

/// Test: Get audio download URLs
/// GET /api/clips/{clip_id}/audio-urls
#[test]
fn test_get_audio_download_urls() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY not set");

    // Use a known clip ID (you may need to update this with a valid ID)
    let test_clip_id = std::env::var("TEST_CLIP_ID").ok();

    if let Some(clip_id) = test_clip_id {
        let url = format!(
            "{}/api/clips/{}/audio-urls",
            base.trim_end_matches('/'),
            clip_id
        );
        let client = create_client(&api_key);

        let resp = client
            .get(&url)
            .send()
            .unwrap_or_else(|e| panic!("request failed: {}", e));

        println!("Audio URL response: {}", resp.status());

        if resp.status().is_success() {
            let body = resp
                .text()
                .unwrap_or_else(|e| panic!("reading body failed: {}", e));

            let download_response: sunocore::models::audio_download::AudioDownloadResponse =
                serde_json::from_str(&body).unwrap_or_else(|e| {
                    panic!(
                        "deserialization failed: {}\nbody: {}",
                        e,
                        &body[..body.len().min(2000)]
                    )
                });

            println!("Clip ID: {}", download_response.clip_id);
            println!("Available URLs: {}", download_response.urls.len());

            for url_info in &download_response.urls {
                println!("  Format: {:?}", url_info.format);
                if let Some(size) = url_info.size_bytes {
                    println!("    Size: {} bytes", size);
                }
                if let Some(ttl) = url_info.expires_in {
                    println!("    Expires in: {} seconds", ttl);
                }
            }

            if let Some(mp3_url) = download_response.get_mp3() {
                println!("MP3 URL available: {}", !mp3_url.url.is_empty());
            }
        }
    } else {
        println!("Skipping audio URL test (TEST_CLIP_ID not set)");
    }
}

/// Test: Generation parameters builder
#[test]
fn test_generation_params_builder() {
    let params = GenerateParams::new("test prompt")
        .with_title("Test Title")
        .with_tags(vec!["ambient".to_string(), "electronic".to_string()])
        .with_instrumental(true)
        .with_model("chirp-v3");

    assert_eq!(params.prompt, "test prompt");
    assert_eq!(params.title, Some("Test Title".to_string()));
    assert!(params.tags.is_some());
    assert_eq!(params.make_instrumental, Some(true));
    assert_eq!(params.model, Some("chirp-v3".to_string()));

    println!("Generation params: {:?}", params);
}

/// Test: Continue generation
/// POST /api/generate/continue/v2
#[test]
fn test_continue_generation() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY not set");

    let test_clip_id = std::env::var("TEST_CLIP_ID").ok();

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

        let params = sunocore::models::generation::ContinueGenerationParams::new(
            &clip_id,
            "continue with a different vibe",
        );

        let resp = client
            .post(&url)
            .json(&params)
            .send()
            .unwrap_or_else(|e| panic!("request failed: {}", e));

        println!("Continue generation response: {}", resp.status());

        if resp.status().is_success() {
            let body = resp
                .text()
                .unwrap_or_else(|e| panic!("reading body failed: {}", e));

            let gen_response: sunocore::models::generation::GenerationResponse =
                serde_json::from_str(&body).unwrap_or_else(|e| {
                    panic!(
                        "deserialization failed: {}\nbody: {}",
                        e,
                        &body[..body.len().min(2000)]
                    )
                });

            println!("Continued generation ID: {}", gen_response.id);
            println!("Status: {}", gen_response.status);
        }
    } else {
        println!("Skipping continue generation test (TEST_CLIP_ID not set)");
    }
}

/// Test: Infill generation
/// POST /api/generate/infill/v2
#[test]
fn test_infill_generation() {
    let _ = dotenv();
    let base = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY not set");

    let test_clip_id = std::env::var("TEST_CLIP_ID").ok();

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

        let params = sunocore::models::generation::InfillGenerationParams::new(
            &clip_id,
            10.0, // start_s
            20.0, // end_s
            "add a drum break here",
        );

        let resp = client
            .post(&url)
            .json(&params)
            .send()
            .unwrap_or_else(|e| panic!("request failed: {}", e));

        println!("Infill generation response: {}", resp.status());

        if resp.status().is_success() {
            let body = resp
                .text()
                .unwrap_or_else(|e| panic!("reading body failed: {}", e));

            let gen_response: sunocore::models::generation::GenerationResponse =
                serde_json::from_str(&body).unwrap_or_else(|e| {
                    panic!(
                        "deserialization failed: {}\nbody: {}",
                        e,
                        &body[..body.len().min(2000)]
                    )
                });

            println!("Infill generation ID: {}", gen_response.id);
            println!("Status: {}", gen_response.status);
        }
    } else {
        println!("Skipping infill generation test (TEST_CLIP_ID not set)");
    }
}

/// Test: Audio format helpers
#[test]
fn test_audio_format_helpers() {
    assert_eq!(AudioFormat::Mp3.extension(), "mp3");
    assert_eq!(AudioFormat::Wav.extension(), "wav");
    assert_eq!(AudioFormat::Stem.extension(), "zip");

    assert_eq!(AudioFormat::Mp3.mime_type(), "audio/mpeg");
    assert_eq!(AudioFormat::Wav.mime_type(), "audio/wav");
    assert_eq!(AudioFormat::Stem.mime_type(), "application/zip");

    assert_eq!(AudioFormat::Mp3.to_string(), "mp3");
    assert_eq!(AudioFormat::Wav.to_string(), "wav");
    assert_eq!(AudioFormat::Stem.to_string(), "stem");

    println!("Audio format helpers: OK");
}
