//! Fragment creation from raw GDELT entries.

use crate::parse::RawEntry;

/// A text fragment ready for assembly.
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Fragment {
    /// Whitespace-split tokens of the fragment text.
    pub words: Vec<String>,
    /// Position decile (0, 10, 20, ..., 90).
    pub pos: i32,
    /// Original date string.
    pub date: String,
}

/// Build a text fragment from a raw GDELT entry.
///
/// Concatenates pre + ngram + post, normalizes whitespace,
/// and applies the slash artifact removal heuristic for early positions.
fn build_fragment(entry: &RawEntry) -> Option<Fragment> {
    let pre = entry.pre.trim();
    let ngram = entry.ngram.trim();
    let post = entry.post.trim();

    let mut parts: Vec<&str> = Vec::with_capacity(3);
    if !pre.is_empty() {
        parts.push(pre);
    }
    if !ngram.is_empty() {
        parts.push(ngram);
    }
    if !post.is_empty() {
        parts.push(post);
    }

    let mut sentence = parts.join(" ");

    // Slash artifact removal for early positions
    if entry.pos < 20 {
        if let Some(idx) = sentence.find(" / ") {
            sentence = sentence[idx + 3..].to_string();
        }
    }

    let words: Vec<String> = sentence
        .split_whitespace()
        .map(|s| s.to_string())
        .collect();

    if words.is_empty() {
        return None;
    }

    Some(Fragment {
        words,
        pos: entry.pos,
        date: entry.date.clone(),
    })
}

/// Create fragments from raw entries for a single URL.
///
/// Sorts by position. No deduplication — the greedy overlap algorithm
/// benefits from duplicate fragments as they provide additional overlap
/// opportunities at different points in the merge sequence.
pub fn create_fragments(entries: &[RawEntry]) -> Vec<Fragment> {
    let mut fragments: Vec<Fragment> = entries
        .iter()
        .filter_map(build_fragment)
        .collect();

    fragments.sort_by(|a, b| {
        a.pos.cmp(&b.pos)
            .then_with(|| b.words.len().cmp(&a.words.len()))
    });

    fragments
}

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

    fn raw(ngram: &str, pos: i32, pre: &str, post: &str) -> RawEntry {
        RawEntry {
            date: "2020-01-01".into(),
            ngram: ngram.into(),
            lang: "en".into(),
            lang_type: 1,
            pos,
            pre: pre.into(),
            post: post.into(),
            url: "http://example.com".into(),
        }
    }

    #[test]
    fn test_basic_fragment_creation() {
        let entries = vec![
            raw("world", 0, "hello", "foo"),
            raw("bar", 10, "baz", "qux"),
        ];
        let frags = create_fragments(&entries);
        assert_eq!(frags.len(), 2);
        assert_eq!(frags[0].words.join(" "), "hello world foo");
    }

    #[test]
    fn test_duplicates_kept() {
        let entries = vec![
            raw("world", 0, "hello", "foo"),
            raw("world", 0, "hello", "foo"),
        ];
        let frags = create_fragments(&entries);
        assert_eq!(frags.len(), 2);
    }

    #[test]
    fn test_slash_artifact_removal() {
        let entries = vec![
            raw("Commerce", 0, "end of article / beginning", "Secretary"),
        ];
        let frags = create_fragments(&entries);
        assert_eq!(frags[0].words.join(" "), "beginning Commerce Secretary");
    }
}
