// E2E integration tests for ollama-api-rs
// These tests connect to a real Ollama instance and test actual API endpoints
// Run with: cargo test --test e2e_test -- --ignored

#[cfg(test)]
mod e2e_tests {
    use oai_sdk::{
        ChatRequest, EmbedInput, EmbedRequest, EmbeddingsRequest, GenerateRequest, Message,
        ModelClient,
    };
    use tokio_stream::StreamExt;

    const OLLAMA_URL: &str = "http://localhost:11434";

    async fn get_test_client() -> ModelClient {
        ModelClient::builder()
            .base_url(OLLAMA_URL.to_string())
            .build()
            .expect("Failed to create client")
    }

    async fn get_test_model() -> String {
        let client = get_test_client().await;
        match client.list_models().await {
            Ok(models) if !models.is_empty() => {
                println!("Found models, using first one: {}", models[0].name);
                models[0].name.clone()
            }
            Ok(_) => {
                println!("No models found, returning default model name");
                "qwen3.6:27b".to_string()
            }
            Err(_) => {
                eprintln!("Could not connect to Ollama");
                "qwen3.6:27b".to_string()
            }
        }
    }

    #[tokio::test]
    #[ignore]
    async fn test_get_version() {
        let client = get_test_client().await;
        let version = client.get_version().await.expect("Failed to get version");
        println!("Ollama version: {}", version.version);
        assert!(!version.version.is_empty());
    }

    #[tokio::test]
    #[ignore]
    async fn test_list_models() {
        let client = get_test_client().await;
        let models = client.list_models().await.expect("Failed to list models");
        println!("Found {} models", models.len());
        assert!(!models.is_empty());
    }

    #[tokio::test]
    #[ignore]
    async fn test_generate_completion() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = GenerateRequest {
            model,
            prompt: "Say a single word: hello".to_string(),
            stream: false,
            ..Default::default()
        };

        let response = client.generate(request).await.expect("Failed to generate");
        println!("Response: {}", response.response);
        assert!(!response.response.is_empty());
        assert!(response.done);
    }

    #[tokio::test]
    #[ignore]
    async fn test_generate_with_think() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = GenerateRequest {
            model,
            prompt: "What is 2+2?".to_string(),
            stream: false,
            think: Some(true),
            ..Default::default()
        };

        let response = client
            .generate(request)
            .await
            .expect("Failed to generate with think");
        println!("Response: {}", response.response);
        assert!(!response.response.is_empty());
    }

    #[tokio::test]
    #[ignore]
    async fn test_generate_streaming() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = GenerateRequest {
            model,
            prompt: "Say hello".to_string(),
            stream: true,
            ..Default::default()
        };

        let mut stream = client
            .generate_stream(request)
            .await
            .expect("Failed to get stream");

        let mut response_text = String::new();
        let mut chunks = 0;

        while let Some(chunk) = stream.next().await {
            let chunk = chunk.expect("Failed to get chunk");
            response_text.push_str(&chunk.response);
            chunks += 1;
        }

        println!("Received {} chunks, response: {}", chunks, response_text);
        assert!(chunks > 0);
        assert!(!response_text.is_empty());
    }

    #[tokio::test]
    #[ignore]
    async fn test_generate_nonexistent_model() {
        let client = get_test_client().await;

        let request = GenerateRequest {
            model: "nonexistent-model-xyz-123".to_string(),
            prompt: "test".to_string(),
            stream: false,
            ..Default::default()
        };

        let result = client.generate(request).await;
        assert!(result.is_err());
        let err = format!("{}", result.unwrap_err());
        assert!(err.contains("not found") || err.contains("404") || err.contains("error"));
    }

    #[tokio::test]
    #[ignore]
    async fn test_chat_completion() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = ChatRequest {
            model,
            messages: vec![Message {
                role: "user".to_string(),
                content: "What is 2+2?".to_string(),
                images: None,
                tool_calls: None,
                tool_name: None,
                thinking: None,
            }],
            stream: false,
            ..Default::default()
        };

        let response = client.chat(request).await.expect("Failed to chat");

        println!("Chat response: {}", response.message.content);
        assert!(!response.message.content.is_empty());
        assert!(response.done);
    }

    #[tokio::test]
    #[ignore]
    async fn test_chat_streaming() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = ChatRequest {
            model,
            messages: vec![Message {
                role: "user".to_string(),
                content: "Count to 3".to_string(),
                images: None,
                tool_calls: None,
                tool_name: None,
                thinking: None,
            }],
            stream: true,
            ..Default::default()
        };

        let mut stream = client
            .chat_stream(request)
            .await
            .expect("Failed to get chat stream");

        let mut response_text = String::new();
        let mut chunks = 0;

        while let Some(chunk) = stream.next().await {
            let chunk = chunk.expect("Failed to get chunk");
            response_text.push_str(&chunk.message.content);
            chunks += 1;
        }

        println!("Chat stream - {} chunks: {}", chunks, response_text);
        assert!(chunks > 0);
        assert!(!response_text.is_empty());
    }

    #[tokio::test]
    #[ignore]
    async fn test_chat_nonexistent_model() {
        let client = get_test_client().await;

        let request = ChatRequest {
            model: "nonexistent-model-xyz-123".to_string(),
            messages: vec![Message {
                role: "user".to_string(),
                content: "test".to_string(),
                images: None,
                tool_calls: None,
                tool_name: None,
                thinking: None,
            }],
            stream: false,
            ..Default::default()
        };

        let result = client.chat(request).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    #[ignore]
    async fn test_embeddings_legacy() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = EmbeddingsRequest {
            model,
            prompt: "Hello world".to_string(),
            ..Default::default()
        };

        let response = client
            .embeddings(request)
            .await
            .expect("Failed to get embeddings");
        assert!(!response.embedding.is_empty());
        println!("Embedding dimensions: {}", response.embedding.len());
    }

    #[tokio::test]
    #[ignore]
    async fn test_embed_batch() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = EmbedRequest {
            model,
            input: EmbedInput::Multiple(vec!["Hello".to_string(), "World".to_string()]),
            ..Default::default()
        };

        let response = client
            .embed(request)
            .await
            .expect("Failed to get batch embeddings");
        assert_eq!(response.embeddings.len(), 2);
        println!("Got {} embeddings", response.embeddings.len());
    }

    #[tokio::test]
    #[ignore]
    async fn test_embed_single() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = EmbedRequest {
            model,
            input: EmbedInput::Single("Embed this text".to_string()),
            ..Default::default()
        };

        let response = client.embed(request).await.expect("Failed to embed");
        assert_eq!(response.embeddings.len(), 1);
        assert!(!response.embeddings[0].is_empty());
    }

    #[tokio::test]
    #[ignore]
    async fn test_show_model() {
        let client = get_test_client().await;
        let model = get_test_model().await;

        let request = oai_sdk::ShowModelRequest {
            model: model.clone(),
            verbose: Some(true),
        };

        let response = client
            .show_model(request)
            .await
            .expect("Failed to show model");

        println!(
            "Model details: {} parameter_size",
            response.details.parameter_size
        );
        // modelfile is only returned when verbose=true, but even then it might be empty for some models
        // So we just check that we got a valid response with parameter_size
        assert!(!response.details.parameter_size.is_empty());
    }

    #[tokio::test]
    #[ignore]
    #[cfg(feature = "local")]
    async fn test_list_running_models() {
        let client = get_test_client().await;
        let models = client.list_running_models().await.unwrap_or_default();

        println!("Running models: {}", models.len());
    }
}
