use std::path::Path;

use regex::Regex;
use std::sync::LazyLock;

use super::cypher_escape;
use crate::graph::KodexGraph;

static NON_ALNUM: LazyLock<Regex> =
    LazyLock::new(|| Regex::new(r"[^A-Za-z0-9_]").expect("invalid regex"));

/// Export graph to Neo4j Cypher MERGE statements.
pub fn to_cypher(graph: &KodexGraph, output_path: &Path) -> std::io::Result<()> {
    let mut lines = vec![
        "// Neo4j Cypher import - generated by kodex".to_string(),
        String::new(),
    ];

    // Nodes
    for id in graph.node_ids() {
        let node = match graph.get_node(id) {
            Some(n) => n,
            None => continue,
        };
        let label_esc = cypher_escape(&node.label);
        let id_esc = cypher_escape(id);
        let ft = node.file_type.to_string();
        let mut ftype = NON_ALNUM.replace_all(&ft, "").to_string();
        if ftype.is_empty() || !ftype.chars().next().unwrap_or('_').is_alphabetic() {
            ftype = "Entity".to_string();
        } else {
            // Capitalize first letter
            let mut chars = ftype.chars();
            ftype = chars.next().unwrap().to_uppercase().to_string() + chars.as_str();
        }
        lines.push(format!(
            "MERGE (n:{ftype} {{id: '{id_esc}', label: '{label_esc}'}});"
        ));
    }

    lines.push(String::new());

    // Edges
    for (src, tgt, edge) in graph.edges() {
        let rel = NON_ALNUM
            .replace_all(&edge.relation.to_uppercase(), "_")
            .to_string();
        let rel = if rel.is_empty() { "RELATES_TO" } else { &rel };
        let conf_esc = cypher_escape(&edge.confidence.to_string());
        let src_esc = cypher_escape(src);
        let tgt_esc = cypher_escape(tgt);
        lines.push(format!(
            "MATCH (a {{id: '{src_esc}'}}), (b {{id: '{tgt_esc}'}}) MERGE (a)-[:{rel} {{confidence: '{conf_esc}'}}]->(b);"
        ));
    }

    if let Some(parent) = output_path.parent() {
        std::fs::create_dir_all(parent)?;
    }
    std::fs::write(output_path, lines.join("\n"))
}
