pub struct PromptEngine {
    pub umbral: usize,
}

impl PromptEngine {
    pub fn new(umbral: usize) -> Self {
        Self { umbral }
    }

    pub fn build_system_context(ctx: &[String]) -> Option<String> {
        if ctx.is_empty() {
            return None;
        }
        let items: Vec<String> = ctx.iter().enumerate()
            .map(|(i, c)| format!("{}. {}", i + 1, c))
            .collect();
        Some(format!("Información conocida de sesiones anteriores:\n{}", items.join("\n")))
    }

    pub fn build_user_prompt(
        &self,
        text: &str,
        text_labels: &[String],
        atink_labels: &[(String, String)],
        question: &str,
    ) -> String {
        let tokens = text.split_whitespace().count();
        if tokens < self.umbral {
            let mut parts = vec![format!("Texto: {}", text)];
            if !text_labels.is_empty() {
                let labels = text_labels.iter()
                    .take(10)
                    .cloned()
                    .collect::<Vec<_>>()
                    .join(", ");
                parts.push(format!("Conceptos relevantes: {}", labels));
            }
            if !atink_labels.is_empty() {
                let links: Vec<String> = atink_labels.iter()
                    .take(10)
                    .map(|(a, b)| format!("{} ↔ {}", a, b))
                    .collect();
                parts.push(format!("Asociaciones: {}", links.join("; ")));
            }
            parts.push(format!("Pregunta: {}", question));
            return parts.join("\n\n");
        }

        let k = if !text_labels.is_empty() {
            format!("K:{}", text_labels.join(","))
        } else {
            String::from("K:")
        };
        let t = if !atink_labels.is_empty() {
            format!("T:{}",
                atink_labels.iter()
                    .map(|(a, b)| format!("{}↔{}", a, b))
                    .collect::<Vec<_>>()
                    .join("|"))
        } else {
            String::from("T:")
        };
        format!("{} | {} | Q:{}", k, t, question)
    }

    pub fn build_user_prompt_sparse(
        sparse_text: &str,
        tokens_selected: usize,
        tokens_total: usize,
        question: &str,
    ) -> String {
        format!(
            "Fragmentos relevantes del documento ({} de {} tokens, {:.1}%):\n{}\n\nPregunta: {}",
            tokens_selected,
            tokens_total,
            if tokens_total > 0 { (tokens_selected as f64 / tokens_total as f64) * 100.0 } else { 0.0 },
            sparse_text,
            question
        )
    }
}

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

    #[test]
    fn system_context_empty() {
        assert!(PromptEngine::build_system_context(&[]).is_none());
    }

    #[test]
    fn system_context_with_items() {
        let ctx = PromptEngine::build_system_context(&["hecho A".into(), "hecho B".into()]);
        assert!(ctx.is_some());
        let s = ctx.unwrap();
        assert!(s.contains("hecho A"));
        assert!(s.contains("hecho B"));
    }

    #[test]
    fn user_prompt_short_no_hex() {
        let engine = PromptEngine::new(500);
        let p = engine.build_user_prompt(
            "short text here",
            &["palabra".into(), "texto".into()],
            &[],
            "pregunta?"
        );
        assert!(p.contains("short text"));
        assert!(p.contains("palabra"));
        assert!(!p.contains("000000"));
        assert!(p.contains("Pregunta:"));
    }

    #[test]
    fn user_prompt_long_format() {
        let engine = PromptEngine::new(500);
        let p = engine.build_user_prompt(
            &"word ".repeat(600),
            &["concepto_a".into()],
            &[("a".into(), "b".into())],
            "q?"
        );
        assert!(p.contains("K:"));
        assert!(p.contains("T:"));
        assert!(p.contains("Q:"));
    }

    #[test]
    fn user_prompt_sparse_format() {
        let p = PromptEngine::build_user_prompt_sparse(
            "fragmentos clave",
            5,
            5000,
            "¿resumen?"
        );
        assert!(p.contains("fragmentos clave"));
        assert!(p.contains("5 de 5000"));
        assert!(p.contains("Pregunta:"));
    }
}
