//! Demonstration of generation services and feed-based polling
//!
//! This example shows:
//! 1. Submitting generation requests
//! 2. Polling the feed for clip updates (using feed middleware approach)
//! 3. Downloading completed audio files
//!
//! Run with: cargo run --example generation_demo

use std::fs;
use std::path::PathBuf;
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::{FeedFilters, FeedRequest, FeedResponse, PresenceFilter, WorkspaceFilter};
use sunocore::validation::env::{suno_api_key_unwrap, suno_base_url_unwrap, workspace_id_unwrap};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _ = dotenv();

    let base_url = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().ok_or("SUNO_API_KEY required")?;
    let workspace_id = workspace_id_unwrap();

    println!("=== Suno Generation Demo ===\n");

    // Setup HTTP 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))?);
    headers.insert("X-Api-Key", HeaderValue::from_str(&api_key)?);

    let client = Client::builder()
        .default_headers(headers.clone())
        .timeout(Duration::from_secs(60))
        .build()?;

    // Step 1: Submit a simple generation
    println!("Step 1: Submitting generation request");
    println!("─────────────────────────────────────");

    let params = GenerateParams::new("a short cheerful ukulele melody")
        .with_title("Happy Ukulele")
        .with_instrumental(true);

    let gen_url = format!("{}/api/generate/v2", base_url.trim_end_matches('/'));
    let resp = client.post(&gen_url).json(&params).send()?;

    if !resp.status().is_success() {
        println!("✗ Generation failed: {}", resp.status());
        return Ok(());
    }

    let gen_response: GenerationResponse = resp.json()?;
    println!("✓ Generation submitted");
    println!("  Request ID: {}", gen_response.id);
    println!("  Clips: {}", gen_response.clips.len());

    for clip in &gen_response.clips {
        println!("    Clip: {} (status: {})", clip.id, clip.status);
    }

    // Step 2: Poll feed to find our clips
    println!("\nStep 2: Polling feed for clip updates");
    println!("─────────────────────────────────────");

    let feed_url = format!("{}/api/feed/v3", base_url.trim_end_matches('/'));
    let feed_request = FeedRequest {
        cursor: None,
        limit: 50,
        filters: FeedFilters {
            disliked: "False".to_string(),
            trashed: "False".to_string(),
            from_studio_project: PresenceFilter {
                presence: "any".to_string(),
            },
            stem: PresenceFilter {
                presence: "any".to_string(),
            },
            workspace: WorkspaceFilter {
                presence: "True".to_string(),
                workspace_id,
            },
        },
    };

    let mut found_clips = Vec::new();
    let clip_ids: Vec<String> = gen_response.clips.iter().map(|c| c.id.clone()).collect();

    // Poll up to 20 times with exponential backoff
    for attempt in 1..=20 {
        let resp = client.post(&feed_url).json(&feed_request).send()?;

        if !resp.status().is_success() {
            println!("Feed request failed: {}", resp.status());
            continue;
        }

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

        if attempt == 1 {
            println!("Feed contains {} clips", feed_response.clips.len());
            println!("\nLooking for our clips:");
            for clip_id in &clip_ids {
                println!("  - {}", clip_id);
            }
            println!("\nFirst 5 clips in feed:");
            for (i, c) in feed_response.clips.iter().take(5).enumerate() {
                let title = if c.title.is_empty() {
                    "(untitled)".to_string()
                } else {
                    c.title[..c.title.len().min(30)].to_string()
                };
                println!("  [{}] {} - {} - audio: {}",
                    i, c.id, title, !c.audio_url.is_empty());
            }
            println!();
        }

        // Check for our clips
        let mut found_this_round = 0;
        for clip_id in &clip_ids {
            if !found_clips.iter().any(|c: &sunocore::models::feed::Clip| c.id.to_string() == *clip_id) {
                if let Some(clip) = feed_response.clips.iter().find(|c| c.id.to_string() == *clip_id) {
                    found_this_round += 1;
                    let has_audio = !clip.audio_url.is_empty();
                    println!("  ✓ Found {} in feed! status={}, has_audio={}",
                        &clip_id[..8], clip.status, has_audio);

                    if has_audio {
                        println!("    Audio: {}...", &clip.audio_url[..clip.audio_url.len().min(60)]);
                        found_clips.push(clip.clone());
                    }
                }
            }
        }

        // Show progress
        print!("Attempt {}: ", attempt);
        if found_this_round > 0 {
            println!("Found {} new clips!", found_this_round);
        } else {
            println!("Clips found: {}/{}", found_clips.len(), clip_ids.len());
        }

        // Stop if we found all clips with audio
        if found_clips.len() >= clip_ids.len() {
            println!("\n✓ All {} clips ready with audio!", found_clips.len());
            break;
        }

        // Exponential backoff
        let wait_ms = (1000.0 * 1.5f64.powi((attempt - 1) as i32)).min(30000.0) as u64;
        println!("  Waiting {}ms before next poll...\n", wait_ms);
        std::thread::sleep(Duration::from_millis(wait_ms));
    }

    if found_clips.is_empty() {
        println!("\n! No clips found with audio URLs yet");
        println!("! Clips may still be processing - check the Suno web UI");
        return Ok(());
    }

    // Step 3: Download audio files
    println!("\nStep 3: Downloading {} audio files", found_clips.len());
    println!("─────────────────────────────────────");

    let output_dir = PathBuf::from("/Users/neil/suno/oxide/target/generated_songs");
    fs::create_dir_all(&output_dir)?;

    let download_client = Client::builder()
        .timeout(Duration::from_secs(60))
        .build()?;

    for (idx, clip) in found_clips.iter().enumerate() {
        let title = clip.title.replace(" ", "_").replace("/", "_");
        let filename = format!("{:02}_{}.mp3", idx + 1, title);
        let output_path = output_dir.join(filename);

        println!("Downloading: {}", clip.title);
        match download_client.get(&clip.audio_url).send() {
            Ok(resp) if resp.status().is_success() => {
                let bytes = resp.bytes()?;
                fs::write(&output_path, &bytes)?;
                println!("  ✓ Saved {} bytes to {:?}", bytes.len(), output_path.file_name());
            }
            Ok(resp) => {
                println!("  ✗ Download failed: HTTP {}", resp.status());
            }
            Err(e) => {
                println!("  ✗ Error: {}", e);
            }
        }
    }

    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    println!("✓ Demo Complete!");
    println!("  Downloaded: {} files", found_clips.len());
    println!("  Location: {:?}", output_dir);
    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");

    Ok(())
}
