use serde::{Deserialize, Serialize};
use crate::types::{Claim, ClaimCheck, ClaimStatus, RelatedClaim};

#[derive(Debug, Deserialize)]
pub struct QueryResponse {
    pub assertions: Vec<AssertionResponse>,
    pub conflict_score: Option<f32>,
}

#[derive(Debug, Deserialize)]
pub struct AssertionResponse {
    pub subject: String,
    pub predicate: String,
    pub object: ObjectValue,
    pub confidence: f32,
    pub source_class: String,
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum ObjectValue {
    Text(String),
    Number(f64),
    Boolean(bool),
    Link(String),
    Image(String),
}

impl ToString for ObjectValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ObjectValue::Text(s) => write!(f, "{}", s),
            ObjectValue::Number(n) => write!(f, "{}", n),
            ObjectValue::Boolean(b) => write!(f, "{}", b),
            ObjectValue::Link(s) => write!(f, "{}", s),
            ObjectValue::Image(s) => write!(f, "[Image: {}]", s),
        }
    }
}

pub struct Client {
    url: String,
    http: reqwest::Client,
}

impl Client {
    pub fn new(url: String) -> Self {
        Self {
            url: url.trim_end_matches('/').to_string(),
            http: reqwest::Client::new(),
        }
    }

    pub async fn check_claim(&self, claim: &Claim) -> Result<ClaimCheck, String> {
        let url = format!("{}/v1/query", self.url);
        
        // Query using Skeptic lens to see conflicts
        let response = self.http.get(&url)
            .query(&[
                ("subject", &claim.subject),
                ("predicate", &claim.predicate),
                ("lens", &"Skeptic".to_string()),
            ])
            .send()
            .await
            .map_err(|e| format!("Request failed: {}", e))?;

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

        let data: QueryResponse = response.json().await
            .map_err(|e| format!("Parse error: {}", e))?;

        let status = if data.assertions.is_empty() {
            ClaimStatus::New
        } else if let Some(score) = data.conflict_score {
            if score > 0.5 {
                ClaimStatus::Contradicts
            } else {
                ClaimStatus::Matches
            }
        } else {
            ClaimStatus::Matches
        };

        let related = data.assertions.into_iter().map(|a| {
            RelatedClaim {
                claim: Claim {
                    subject: a.subject,
                    predicate: a.predicate,
                    object: a.object.to_string(),
                    confidence: a.confidence,
                    quote: "".to_string(),
                    source: Some(a.source_class),
                },
                relationship: "existing".to_string(),
                source: "stemedb".to_string(),
            }
        }).collect();

        Ok(ClaimCheck {
            claim: claim.clone(),
            status,
            related,
        })
    }

    pub async fn save_claim(&self, claim: &Claim) -> Result<(), String> {
        let url = format!("{}/v1/assert", self.url);
        
        let body = serde_json::json!({
            "subject": claim.subject,
            "predicate": claim.predicate,
            "object": {
                "type": "Text",
                "value": claim.object
            },
            "confidence": claim.confidence,
            "source_class": "Anecdotal", // Default for Disputed
        });

        let response = self.http.post(&url)
            .json(&body)
            .send()
            .await
            .map_err(|e| format!("Request failed: {}", e))?;

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

        Ok(())
    }
}
