use tauri::State;
use tracing::instrument;

use crate::llm::{chunk_text, create_client, deduplicate_claims, ChunkConfig, LlmConfig};
use crate::types::{Claim, ClaimCheck, ClaimStatus};

use super::settings::SettingsState;

/// Extract claims from text using the configured LLM provider.
#[tauri::command]
#[instrument(skip(state), fields(text_len = text.len()))]
pub async fn extract_claims(
    state: State<'_, SettingsState>,
    text: String,
) -> Result<Vec<Claim>, String> {
    tracing::info!(text_len = text.len(), "Extracting claims from text");

    let settings = state.0.lock().map_err(|e| format!("Failed to read settings: {}", e))?.clone();

    let api_key = settings
        .api_key
        .ok_or_else(|| format!("Please add your {} API key in Settings", settings.llm_provider))?;

    let config = LlmConfig { api_key, timeout_secs: 60 };

    let client = create_client(&settings.llm_provider, config).map_err(|e| e.to_string())?;

    // Handle batching for long text (>4000 chars)
    let claims = if text.len() > 4000 {
        let chunks = chunk_text(&text, &ChunkConfig::default());
        tracing::info!(chunk_count = chunks.len(), "Text is long, processing in chunks");

        let mut all_claims = Vec::new();
        for (i, chunk) in chunks.iter().enumerate() {
            tracing::info!(chunk_index = i, chunk_len = chunk.len(), "Processing chunk");
            match client.extract_claims(chunk).await {
                Ok(c) => all_claims.extend(c),
                Err(e) => return Err(map_llm_error_to_user_message(&e)),
            }
        }
        deduplicate_claims(all_claims)
    } else {
        client.extract_claims(&text).await.map_err(|e| map_llm_error_to_user_message(&e))?
    };

    tracing::info!(claim_count = claims.len(), "Extraction complete");
    Ok(claims)
}

/// Test the LLM connection with the current settings.
#[tauri::command]
#[instrument(skip(state))]
pub async fn test_llm_connection(state: State<'_, SettingsState>) -> Result<String, String> {
    let settings = state.0.lock().map_err(|e| format!("Failed to read settings: {}", e))?.clone();

    let api_key = settings.api_key.ok_or_else(|| "API key not configured".to_string())?;

    let config = LlmConfig { api_key, timeout_secs: 10 };

    let client = create_client(&settings.llm_provider, config).map_err(|e| e.to_string())?;

    client.test_connection().await.map_err(|e| map_llm_error_to_user_message(&e))?;

    Ok(format!("Connected to {}", settings.llm_provider))
}

/// Map LLM errors to user-friendly messages.
fn map_llm_error_to_user_message(e: &crate::llm::LlmError) -> String {
    use crate::llm::LlmError;
    match e {
        LlmError::MissingApiKey(provider) => {
            format!("Please add your {} API key in Settings", provider)
        }
        LlmError::AuthenticationFailed => {
            "Invalid API key. Check your key in Settings.".to_string()
        }
        LlmError::RateLimited { retry_after_secs } => {
            format!("Rate limit reached. Please wait {} seconds and try again.", retry_after_secs)
        }
        LlmError::ServiceUnavailable(_) => "AI service temporarily unavailable.".to_string(),
        LlmError::Timeout(_) => "Request timed out. Try shorter text.".to_string(),
        LlmError::Network(_) => "Network error. Check your connection.".to_string(),
        LlmError::InvalidResponse(msg) => format!("Unexpected response from AI: {}", msg),
        LlmError::JsonParse(_) => {
            "Failed to parse AI response. Try again or use simpler text.".to_string()
        }
        LlmError::UnsupportedProvider(p) => format!("Unsupported provider: {}", p),
    }
}

/// Check claims against the knowledge graph.
#[tauri::command]
pub async fn check_claims(claims: Vec<Claim>) -> Result<Vec<ClaimCheck>, String> {
    tracing::info!(count = claims.len(), "Checking claims");

    // TODO: Week 3 - Check against Episteme
    Ok(claims
        .into_iter()
        .map(|claim| ClaimCheck { claim, status: ClaimStatus::New, related: vec![] })
        .collect())
}

/// Save claims to the knowledge graph.
#[tauri::command]
pub async fn save_claims(claims: Vec<Claim>) -> Result<usize, String> {
    let count = claims.len();
    tracing::info!(count, "Saving claims");
    // TODO: Week 3 - Save to Episteme
    Ok(count)
}

/// Get the current claim count.
#[tauri::command]
pub async fn get_claim_count() -> Result<usize, String> {
    // TODO: Week 3 - Query Episteme
    Ok(0)
}
