use std::collections::HashSet;

use crate::types::Claim;

pub struct ChunkConfig {
    pub max_chunk_size: usize,
    pub overlap_size: usize,
}

impl Default for ChunkConfig {
    fn default() -> Self {
        Self { max_chunk_size: 3000, overlap_size: 500 }
    }
}

/// Split text into chunks at sentence boundaries with overlap.
pub fn chunk_text(text: &str, config: &ChunkConfig) -> Vec<String> {
    if text.len() <= config.max_chunk_size {
        return vec![text.to_string()];
    }

    let mut chunks = Vec::new();
    let mut start = 0;

    while start < text.len() {
        let end = (start + config.max_chunk_size).min(text.len());

        // Find sentence boundary (look for . ! ? followed by space or end)
        let chunk_end =
            text[start..end].rfind(['.', '!', '?']).map(|i| start + i + 1).unwrap_or(end);

        chunks.push(text[start..chunk_end].to_string());

        // Move start back by overlap, but ensure we make progress
        let next_start = chunk_end.saturating_sub(config.overlap_size);
        start = if next_start <= start {
            chunk_end // Ensure we always make progress
        } else {
            next_start
        };
    }

    chunks
}

/// Remove duplicate claims based on subject-predicate-object key.
pub fn deduplicate_claims(claims: Vec<Claim>) -> Vec<Claim> {
    let mut seen = HashSet::new();
    claims
        .into_iter()
        .filter(|c| {
            let key = format!(
                "{}|{}|{}",
                c.subject.to_lowercase(),
                c.predicate.to_lowercase(),
                c.object.to_lowercase()
            );
            seen.insert(key)
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_short_text_not_chunked() {
        let text = "This is a short sentence.";
        let config = ChunkConfig::default();
        let chunks = chunk_text(text, &config);
        assert_eq!(chunks.len(), 1);
        assert_eq!(chunks[0], text);
    }

    #[test]
    fn test_long_text_chunked() {
        let text = "A".repeat(4000);
        let config = ChunkConfig { max_chunk_size: 1000, overlap_size: 100 };
        let chunks = chunk_text(&text, &config);
        assert!(chunks.len() > 1);
    }

    #[test]
    fn test_deduplicate_claims() {
        let claims = vec![
            Claim {
                subject: "Apple".to_string(),
                predicate: "is".to_string(),
                object: "a company".to_string(),
                confidence: 1.0,
                quote: "quote1".to_string(),
                source: None,
            },
            Claim {
                subject: "apple".to_string(), // Different case
                predicate: "IS".to_string(),
                object: "A Company".to_string(),
                confidence: 0.9,
                quote: "quote2".to_string(),
                source: None,
            },
            Claim {
                subject: "Google".to_string(),
                predicate: "is".to_string(),
                object: "a company".to_string(),
                confidence: 1.0,
                quote: "quote3".to_string(),
                source: None,
            },
        ];

        let deduped = deduplicate_claims(claims);
        assert_eq!(deduped.len(), 2);
        assert_eq!(deduped[0].subject, "Apple");
        assert_eq!(deduped[1].subject, "Google");
    }
}
