//! Integration tests for generation services using Tower
//!
//! Tests the actual Tower service implementations with real API

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, GenerationResponse, PollingStatus, ClipStatus};
use sunocore::services::generation::{
    SubmitGeneration, SubmitGenerationRequest, PollGeneration, PollGenerationRequest,
    BatchPoll, BatchPollRequest,
};
use sunocore::validation::env::{suno_api_key_unwrap, suno_base_url_unwrap};
use tower::{Service, ServiceExt};

// Tower service adapter for reqwest
#[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::test]
async fn test_submit_generation_tower() {
    let _ = dotenv();

    let base_url = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    let client = reqwest::Client::builder()
        .timeout(std::time::Duration::from_secs(60))
        .build()
        .unwrap();

    let http_svc = ReqwestSvc { client };
    let mut gen_svc = SubmitGeneration::new(http_svc);

    let params = GenerateParams::new("a short test melody for tower service")
        .with_title("Tower Test");

    let request = SubmitGenerationRequest::new(base_url.clone(), params, api_key.clone());

    let result = gen_svc.ready().await.unwrap().call(request).await;

    match result {
        Ok(response) => {
            println!("✓ Generation submitted via Tower service");
            println!("  Request ID: {}", response.id);
            println!("  Clips: {}", response.clips.len());

            assert!(!response.clips.is_empty(), "Should have at least one clip");

            for clip in &response.clips {
                println!("  Clip: {} (status: {})", clip.id, clip.status);
            }
        }
        Err(e) => {
            panic!("Generation failed: {}", e);
        }
    }
}

#[tokio::test]
async fn test_poll_generation_tower() {
    let _ = dotenv();

    let base_url = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    // First submit a generation
    let client = reqwest::Client::builder()
        .timeout(std::time::Duration::from_secs(60))
        .build()
        .unwrap();

    let http_svc = ReqwestSvc { client: client.clone() };
    let mut gen_svc = SubmitGeneration::new(http_svc);

    let params = GenerateParams::new("test polling").with_title("Poll Test");
    let submit_req = SubmitGenerationRequest::new(base_url.clone(), params, api_key.clone());
    let gen_response = gen_svc.ready().await.unwrap().call(submit_req).await.unwrap();

    let clip_id = gen_response.clips[0].id.clone();
    println!("Generated clip: {}", clip_id);

    // Now poll for it using the PollGeneration service
    let http_svc2 = ReqwestSvc { client };
    let mut poll_svc = PollGeneration::new(http_svc2);

    let poll_req = PollGenerationRequest::new(base_url, clip_id.clone(), api_key);

    // Poll a few times
    for attempt in 1..=5 {
        let result = poll_svc.ready().await.unwrap().call(poll_req.clone()).await;

        match result {
            Ok(status) => {
                println!("Attempt {}: status={}, has_audio={}",
                    attempt, status.status, status.audio_url.is_some());

                if status.status == ClipStatus::Complete && status.audio_url.is_some() {
                    println!("✓ Clip completed!");
                    return;
                }
            }
            Err(e) => {
                eprintln!("Poll error: {}", e);
            }
        }

        tokio::time::sleep(std::time::Duration::from_secs(2)).await;
    }

    println!("! Clip not complete within 5 attempts (normal for real generation)");
}

#[tokio::test]
async fn test_batch_poll_tower() {
    let _ = dotenv();

    let base_url = suno_base_url_unwrap();
    let api_key = suno_api_key_unwrap().expect("SUNO_API_KEY required");

    // Submit a generation to get clip IDs
    let client = reqwest::Client::builder()
        .timeout(std::time::Duration::from_secs(60))
        .build()
        .unwrap();

    let http_svc = ReqwestSvc { client: client.clone() };
    let mut gen_svc = SubmitGeneration::new(http_svc);

    let params = GenerateParams::new("batch poll test");
    let submit_req = SubmitGenerationRequest::new(base_url.clone(), params, api_key.clone());
    let gen_response = gen_svc.ready().await.unwrap().call(submit_req).await.unwrap();

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

    // Now batch poll
    let http_svc2 = ReqwestSvc { client };
    let mut batch_poll_svc = BatchPoll::new(http_svc2);

    let batch_req = BatchPollRequest::new(base_url, clip_ids.clone(), api_key);

    let result = batch_poll_svc.ready().await.unwrap().call(batch_req).await;

    match result {
        Ok(statuses) => {
            println!("✓ Batch poll successful");
            println!("  Returned {} statuses", statuses.len());

            for status in &statuses {
                println!("  {}: {}, audio: {}",
                    &status.clip_id[..8],
                    status.status,
                    status.audio_url.is_some());
            }

            assert_eq!(statuses.len(), clip_ids.len(), "Should return status for all clips");
        }
        Err(e) => {
            panic!("Batch poll failed: {}", e);
        }
    }
}
