use crate::config::get_config;
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Serialize, Deserialize)]
pub struct SearchRequest {
    pub query: String,
    pub limit: Option<usize>,
    pub mode: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SearchResult {
    pub query: String,
    pub count: usize,
    pub hits: Vec<SearchHit>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SearchHit {
    pub id: String,
    pub vid: String,
    pub start_frame: i64,
    pub end_frame: i64,
    pub fps: f64,
    pub start: f64,
    pub end: f64,
    pub text: String,
    pub score: f64,
    #[serde(default)]
    pub title: Option<String>,
    #[serde(default)]
    pub file_path: Option<String>,
    #[serde(default)]
    pub has_visual_stats: Option<bool>,
    #[serde(default)]
    pub parent_id: Option<String>,
}

#[tauri::command]
pub async fn search_videos(
    query: String,
    limit: Option<usize>,
    mode: Option<String>,
) -> Result<SearchResult, String> {
    let config = get_config();
    let client = reqwest::Client::new();

    let search_mode = mode.unwrap_or_else(|| "vector".to_string());

    let request_body = SearchRequest {
        query: query.clone(),
        limit: limit.or(Some(10)),
        mode: Some(search_mode.clone()),
    };

    let url = format!("{}/api/v1/search", config.api_base_url);

    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .header("x-api-key", &config.api_key)
        .json(&request_body)
        .send()
        .await
        .map_err(|e| format!("Request failed: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("API error: {}", response.status()));
    }

    // Parse as generic Value to handle mapping manually
    let json: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))?;

    // Map Backend Response to Frontend SearchResult
    // Backend: { "query": "...", "results": [ ... ], "total": N, ... }
    // Frontend: { "query": "...", "hits": [ ... ], "count": N }
    
    let backend_results = json.get("results").and_then(|r| r.as_array()).cloned().unwrap_or_default();
    let total = json.get("total").and_then(|t| t.as_u64()).unwrap_or(0) as usize;

    let hits: Vec<SearchHit> = backend_results.into_iter().filter_map(|item| {
        Some(SearchHit {
            id: item.get("chunk_id").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            vid: item.get("uuid").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            start_frame: item.get("start_frame").and_then(|v| v.as_i64()).unwrap_or(0),
            end_frame: item.get("end_frame").and_then(|v| v.as_i64()).unwrap_or(0),
            fps: item.get("fps").and_then(|v| v.as_f64()).unwrap_or(30.0),
            start: item.get("start_time").and_then(|v| v.as_f64()).unwrap_or(0.0),
            end: item.get("end_time").and_then(|v| v.as_f64()).unwrap_or(0.0),
            text: item.get("text").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            score: item.get("score").and_then(|v| v.as_f64()).unwrap_or(0.0),
            title: item.get("file_name").and_then(|v| v.as_str()).map(|s| s.to_string()),
            file_path: item.get("file_path").and_then(|v| v.as_str()).map(|s| s.to_string()),
            has_visual_stats: item.get("visual_stats").map(|_| true),
            parent_id: item.get("parent_chunk_id").and_then(|v| v.as_str()).map(|s| s.to_string()),
        })
    }).collect();

    Ok(SearchResult {
        query: json.get("query").and_then(|v| v.as_str()).unwrap_or("").to_string(),
        count: total,
        hits,
    })
}

#[tauri::command]
pub async fn search_chunks(query: String, uuid: Option<String>) -> Result<SearchResult, String> {
    let config = get_config();
    let client = reqwest::Client::new();

    // Backend expects uuid in the body, not query params
    let url = format!("{}/api/v1/search", config.api_base_url);
    
    let mut request_body = serde_json::json!({
        "query": query,
        "limit": 10
    });
    
    if let Some(vid) = uuid {
        request_body["uuid"] = serde_json::json!(vid);
    }

    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .header("x-api-key", &config.api_key)
        .json(&request_body)
        .send()
        .await
        .map_err(|e| format!("Request failed: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("API error: {}", response.status()));
    }

    // Parse raw JSON to handle structure mapping
    let json: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("Failed to parse response: {}", e))?;

    // Backend returns "total", frontend expects "count"
    let count = json.get("total").and_then(|v| v.as_u64()).unwrap_or(0) as usize;

    // Backend returns "results", frontend expects "hits"
    let results = json.get("results").and_then(|v| v.as_array()).cloned().unwrap_or_default();
    
    let hits: Vec<SearchHit> = results.into_iter().filter_map(|item| {
        Some(SearchHit {
            id: item.get("chunk_id").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            vid: item.get("uuid").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            start_frame: item.get("start_frame").and_then(|v| v.as_i64()).unwrap_or(0),
            end_frame: item.get("end_frame").and_then(|v| v.as_i64()).unwrap_or(0),
            fps: item.get("fps").and_then(|v| v.as_f64()).unwrap_or(30.0),
            start: item.get("start_time").and_then(|v| v.as_f64()).unwrap_or(0.0),
            end: item.get("end_time").and_then(|v| v.as_f64()).unwrap_or(0.0),
            text: item.get("text").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            score: item.get("score").and_then(|v| v.as_f64()).unwrap_or(0.0),
            title: item.get("file_name").and_then(|v| v.as_str()).map(|s| s.to_string()),
            file_path: item.get("file_path").and_then(|v| v.as_str()).map(|s| s.to_string()),
            has_visual_stats: item.get("visual_stats").map(|_| true),
            parent_id: item.get("parent_chunk_id").and_then(|v| v.as_str()).map(|s| s.to_string()),
        })
    }).collect();

    Ok(SearchResult {
        query: json.get("query").and_then(|v| v.as_str()).unwrap_or("").to_string(),
        count,
        hits,
    })
}