use std::pin::Pin;

use futures::{future::BoxFuture, Stream, StreamExt};
use rig::{
    client::completion::CompletionClient,
    completion::{CompletionModel, Message},
    providers::openai,
    streaming::StreamedAssistantContent,
};

use crate::agent::{AgentRuntimeConfig, ProviderMode};
use crate::error::AppError;

const SYSTEM_PROMPT: &str = "You are MosaicIQ's terminal chat assistant. Answer concisely in plain text. Do not claim to run tools, commands, or file operations. If the request is unclear, ask a short clarifying question.";

/// Streaming text output from the upstream chat provider.
pub type ChatGatewayStream = Pin<Box<dyn Stream<Item = Result<String, AppError>> + Send>>;

/// Trait used by the agent service so tests can inject a deterministic gateway.
pub trait ChatGateway: Clone + Send + Sync + 'static {
    /// Start a streaming chat turn for the given config, prompt, and prior history.
    fn stream_chat(
        &self,
        runtime: AgentRuntimeConfig,
        prompt: String,
        history: Vec<Message>,
    ) -> BoxFuture<'static, Result<ChatGatewayStream, AppError>>;
}

/// Production Rig-backed gateway using the OpenAI-compatible chat completions API.
#[derive(Debug, Clone, Default)]
pub struct RigChatGateway;

impl ChatGateway for RigChatGateway {
    fn stream_chat(
        &self,
        runtime: AgentRuntimeConfig,
        prompt: String,
        history: Vec<Message>,
    ) -> BoxFuture<'static, Result<ChatGatewayStream, AppError>> {
        Box::pin(async move {
            let _task_profile = runtime.task_profile;
            let api_key = match runtime.provider_mode {
                ProviderMode::Remote => runtime.api_key.unwrap_or_default(),
                ProviderMode::Local => "local".to_string(),
            };
            let client = openai::CompletionsClient::builder()
                .api_key(api_key)
                .base_url(&runtime.base_url)
                .build()
                .map_err(|error| AppError::ProviderInit(error.to_string()))?;

            let model = client.completion_model(runtime.model);

            let upstream = model
                .completion_request(Message::user(prompt))
                .messages(history)
                .preamble(SYSTEM_PROMPT.to_string())
                .temperature(0.2)
                .stream()
                .await
                .map_err(|error| AppError::ProviderRequest(error.to_string()))?;

            let stream = upstream.filter_map(|item| async move {
                match item {
                    Ok(StreamedAssistantContent::Text(text)) => Some(Ok(text.text)),
                    Ok(_) => None,
                    Err(error) => Some(Err(AppError::ProviderRequest(error.to_string()))),
                }
            });

            let stream: ChatGatewayStream = Box::pin(stream);
            Ok(stream)
        })
    }
}
