//! LLM provider trait and common types

use crate::error::Result;
use async_trait::async_trait;
use futures::stream::Stream;
use serde::{Deserialize, Serialize};
use std::pin::Pin;

/// Role of a message in conversation
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum MessageRole {
    User,
    Assistant,
    System,
}

/// A single message in the conversation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmMessage {
    pub role: MessageRole,
    pub content: String,
}

/// Request to LLM provider
#[derive(Debug, Clone)]
pub struct LlmRequest {
    pub model: String,
    pub messages: Vec<LlmMessage>,
    pub max_tokens: Option<usize>,
    pub temperature: Option<f64>,
    pub system: Option<String>,
}

/// Response from LLM provider
#[derive(Debug, Clone)]
pub struct LlmResponse {
    pub content: String,
    pub model: String,
    pub usage: Option<TokenUsage>,
}

/// Streaming chunk from LLM
#[derive(Debug, Clone)]
pub enum StreamChunk {
    /// Text content delta
    Content(String),
    /// Stream completed with final metadata
    Done(StreamMetadata),
    /// Error occurred
    Error(String),
}

/// Metadata when stream completes
#[derive(Debug, Clone)]
pub struct StreamMetadata {
    pub model: String,
    pub usage: Option<TokenUsage>,
}

/// Token usage statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenUsage {
    pub input_tokens: usize,
    pub output_tokens: usize,
    pub total_tokens: usize,
}

/// Stream type alias
pub type LlmStream = Pin<Box<dyn Stream<Item = Result<StreamChunk>> + Send>>;

/// LLM provider trait - implemented by Claude, OpenAI, Gemini, etc.
#[async_trait]
pub trait LlmProvider: Send + Sync {
    /// Execute a completion request
    async fn complete(&self, request: LlmRequest) -> Result<LlmResponse>;

    /// Stream a completion request
    async fn stream(&self, request: LlmRequest) -> Result<LlmStream>;

    /// Get the provider name
    fn name(&self) -> &str;
}
