use std::time::Duration;

use dotenvy::dotenv;
use reqwest::blocking::Client;
use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue};
use sunocore::models::downbeats::{Downbeats, DownbeatsState};
use sunocore::validation::env::{suno_api_key_unwrap, suno_base_url_unwrap};
use uuid::Uuid;

// Integration test: fetch from <SUNO_BASE_URL>/api/gen/<clip_id>/downbeats
// and ensure it deserializes into Downbeats.
//
// Disabled by default so CI/local runs don't fail if the backend isn't running.
// Run with: cargo test --test integration_downbeats -- --ignored
#[test]
fn fetch_and_deserialize_downbeats() {
    // Load .env for SUNO_BASE_URL and SUNO_API_KEY
    let _ = dotenv();

    let base = suno_base_url_unwrap();

    // This clip ID is from a real staging project that has downbeats data
    let clip_id = "012ad0f4-d29d-4f5f-a141-83f11f94d114".to_string();

    let _expected_uuid =
        Uuid::parse_str(&clip_id).expect("clip_id must be a valid UUID (or set it in .env)");

    let url = format!(
        "{}/api/gen/{}/downbeats",
        base.trim_end_matches('/'),
        clip_id
    );

    let mut headers = HeaderMap::new();
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));

    let api_key = suno_api_key_unwrap();

    if let Some(api_key) = api_key.as_ref() {
        // Send both common auth styles to maximize compatibility with local/staging
        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"),
        );
    }

    let client = Client::builder()
        .default_headers(headers)
        .timeout(Duration::from_secs(20))
        .build()
        .expect("build http client");

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

    assert!(
        resp.status().is_success(),
        "HTTP {} fetching {}. Did you set SUNO_API_KEY and run the backend at SUNO_BASE_URL?",
        resp.status(),
        url
    );

    // Read text then deserialize for better error messages when schema drifts.
    let body = resp
        .text()
        .unwrap_or_else(|e| panic!("reading body from {} failed: {}", url, e));
    let downbeats: Downbeats = serde_json::from_str(&body).unwrap_or_else(|e| {
        panic!(
            "deserialization failed for {}: {}\nbody: {}",
            url,
            e,
            &body[..body.len().min(2000)]
        )
    });

    // Basic sanity checks
    assert_eq!(
        downbeats.state,
        DownbeatsState::Complete,
        "downbeats should be in complete state"
    );
    assert!(
        downbeats.downbeats.is_some(),
        "expected downbeats array to be present"
    );

    if let Some(db) = &downbeats.downbeats {
        assert!(db.len() > 0, "expected at least one downbeat");
        // Each downbeat should be a pair of [time, beat_number]
        for beat in db {
            assert_eq!(beat.len(), 2, "each downbeat should have 2 elements");
            assert!(beat[0] >= 0.0, "time should be non-negative");
            assert!(
                beat[1] >= 1.0 && beat[1] <= 4.0,
                "beat number should be 1-4"
            );
        }
    }
}
