nitpicker 0.3.0

Multi-reviewer code review using LLMs with parallel agents and debate mode
use crate::config::{AggregatorConfig, ProviderType, ReviewerConfig};
use crate::gemini_proxy::GeminiProxyClient;
use crate::llm::{LLMClient, LLMClientDyn, LLMProvider, WithRetryExt};
use eyre::Result;
use std::sync::Arc;

pub fn needs_gemini_oauth(provider: &ProviderType, auth: Option<&str>) -> bool {
    provider.is_gemini() && auth == Some("oauth")
}

pub fn reviewer_needs_gemini_oauth(reviewer: &ReviewerConfig) -> bool {
    needs_gemini_oauth(&reviewer.provider, reviewer.auth.as_deref())
}

pub fn aggregator_needs_gemini_oauth(agg: &AggregatorConfig) -> bool {
    needs_gemini_oauth(&agg.provider, agg.auth.as_deref())
}

pub fn provider_from_config(
    provider: &ProviderType,
    base_url: Option<&str>,
    api_key_env: Option<&str>,
) -> Result<LLMProvider> {
    match provider {
        ProviderType::Anthropic => Ok(LLMProvider::Anthropic {
            base_url: base_url.map(str::to_string),
            api_key_env: api_key_env.map(str::to_string),
        }),
        ProviderType::Gemini => Ok(LLMProvider::Gemini),
        ProviderType::OpenAi => Ok(LLMProvider::OpenAi {
            base_url: base_url.map(str::to_string),
            api_key_env: api_key_env.map(str::to_string),
        }),
        ProviderType::OpenRouter => Ok(LLMProvider::OpenRouter {
            api_key_env: api_key_env.unwrap_or("OPENROUTER_API_KEY").to_string(),
        }),
    }
}

pub fn build_reviewer_client(
    reviewer: &ReviewerConfig,
    gemini_proxy: Option<&GeminiProxyClient>,
) -> Result<Arc<dyn LLMClientDyn>> {
    if reviewer_needs_gemini_oauth(reviewer) {
        let proxy_url = gemini_proxy
            .map(|p| p.base_url())
            .ok_or_else(|| eyre::eyre!("Gemini proxy required for OAuth but not available"))?;
        return crate::llm::create_gemini_client_with_proxy(&proxy_url);
    }

    Ok(provider_from_config(
        &reviewer.provider,
        reviewer.base_url.as_deref(),
        reviewer.api_key_env.as_deref(),
    )?
    .client_from_env()?
    .with_retry()
    .into_arc())
}

pub fn build_aggregator_client(
    agg: &AggregatorConfig,
    gemini_proxy: Option<&GeminiProxyClient>,
) -> Result<Arc<dyn LLMClientDyn>> {
    if aggregator_needs_gemini_oauth(agg) {
        let proxy_url = gemini_proxy
            .map(|p| p.base_url())
            .ok_or_else(|| eyre::eyre!("Gemini proxy required for OAuth but not available"))?;
        return crate::llm::create_gemini_client_with_proxy(&proxy_url);
    }

    Ok(provider_from_config(
        &agg.provider,
        agg.base_url.as_deref(),
        agg.api_key_env.as_deref(),
    )?
    .client_from_env()?
    .with_retry()
    .into_arc())
}