//! Complete generation flow using ONLY sunocore Tower services
//!
//! Run with: cargo run --example full_generation_flow

use std::fs;
use std::path::PathBuf;
use std::time::Duration;

use bytes::Bytes;
use dotenvy::dotenv;
use futures_util::future::BoxFuture;
use http::{Request, Response, StatusCode};
use http_body_util::Full;
use sunocore::models::generation::{GenerateParams, ClipStatus};
use sunocore::services::generation::{
    SubmitGeneration, SubmitGenerationRequest,
    BatchPoll, BatchPollRequest,
};
use sunocore::validation::env::{suno_api_key_unwrap, suno_base_url_unwrap};
use tower::{Service, ServiceExt};

#[derive(Clone)]
struct ReqwestSvc {
    client: reqwest::Client,
}

impl Service<Request<Full<Bytes>>> for ReqwestSvc {
    type Response = Response<Full<Bytes>>;
    type Error = reqwest::Error;
    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
        std::task::Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request<Full<Bytes>>) -> Self::Future {
        let client = self.client.clone();
        let method = req.method().clone();
        let uri = req.uri().to_string();
        let headers = req.headers().clone();
        let body = req.into_body();

        Box::pin(async move {
            use http_body_util::BodyExt;
            let body_bytes = body.collect().await.unwrap().to_bytes();
            let mut builder = client.request(method, uri);
            for (name, value) in headers.iter() {
                builder = builder.header(name, value);
            }
            if !body_bytes.is_empty() {
                builder = builder.body(body_bytes.to_vec());
            }
            let res = builder.send().await?;
            let status = res.status();
            let body = res.bytes().await?;
            let mut resp = Response::new(Full::new(Bytes::from(body)));
            *resp.status_mut() = StatusCode::from_u16(status.as_u16()).unwrap();
            Ok(resp)
        })
    }
}

#[tokio::main]
async 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")?;

    println!("=== Tower Services Only ===\n");

    let client = reqwest::Client::builder().timeout(Duration::from_secs(60)).build()?;
    let http_svc = ReqwestSvc { client: client.clone() };

    // Step 1: SubmitGeneration
    let mut submit_svc = SubmitGeneration::new(http_svc.clone());
    let params = GenerateParams::new("peaceful rain sounds").with_title("Rain");
    let submit_req = SubmitGenerationRequest::new(base_url.clone(), params, api_key.clone());
    let gen_response = submit_svc.ready().await?.call(submit_req).await?;

    println!("✓ SubmitGeneration: {} clips", gen_response.clips.len());
    let clip_ids: Vec<String> = gen_response.clips.iter().map(|c| c.id.clone()).collect();

    // Step 2: BatchPoll
    let mut poll_svc = BatchPoll::new(http_svc);
    let mut backoff_ms = 1000u64;
    let mut audio_urls = Vec::new();

    for attempt in 1..=20 {
        let poll_req = BatchPollRequest::new(base_url.clone(), clip_ids.clone(), api_key.clone());
        let statuses = poll_svc.ready().await?.call(poll_req).await?;

        let ready = statuses.iter().filter(|s| s.status == ClipStatus::Complete && s.audio_url.is_some()).count();
        print!("  Poll {}: {}/{} ", attempt, ready, clip_ids.len());

        // Debug: show what we got
        if attempt == 1 || attempt % 5 == 0 {
            println!();
            for (i, s) in statuses.iter().enumerate() {
                println!("    Clip {}: status={:?}, audio={}",
                    i, s.status, s.audio_url.is_some());
            }
            print!("    ");
        }

        if ready >= clip_ids.len() {
            println!("✓");
            audio_urls = statuses.into_iter().filter_map(|s| s.audio_url).collect();
            break;
        }

        println!("waiting {}ms", backoff_ms);
        tokio::time::sleep(Duration::from_millis(backoff_ms)).await;
        backoff_ms = ((backoff_ms as f64 * 1.5) as u64).min(30000);
    }

    // Step 3: Download
    if !audio_urls.is_empty() {
        let output_dir = PathBuf::from("/Users/neil/suno/oxide/target/generated_songs");
        fs::create_dir_all(&output_dir)?;
        let dl_client = reqwest::Client::builder().timeout(Duration::from_secs(60)).build()?;

        for (i, url) in audio_urls.iter().enumerate() {
            let path = output_dir.join(format!("{:02}_Rain.mp3", i + 1));
            let bytes = dl_client.get(url).send().await?.bytes().await?;
            fs::write(&path, &bytes)?;
            println!("✓ Downloaded {} bytes", bytes.len());
        }

        println!("\n✓ Complete! Files in {:?}", output_dir);
    }

    Ok(())
}
