use agileplus_domain::builder::*;
use agileplus_domain::intent_graph::*;
use agileplus_trace_validator::validate_intent_graph;
use anyhow::Result;
use chrono::Utc;

use super::extract::{compute_confidence, decompose_features, extract_intent_title, slugify};
use super::types::{ConvertRequest, ConvertResponse};

/// Convert a natural language prompt into an intent graph.
///
/// Steps:
/// 1. Intent extraction (Intent node)
/// 2. Feature decomposition (Feature nodes)
/// 3. Plan generation (Plan node)
/// 4. Edge creation (relationships between nodes)
/// 5. Confidence scoring + validation
pub fn convert_prompt_to_graph(req: &ConvertRequest) -> Result<ConvertResponse> {
    let start = std::time::Instant::now();
    let source = req
        .source_system
        .clone()
        .unwrap_or_else(|| "mcp-intent".to_string());
    let agent = req
        .agent_id
        .clone()
        .unwrap_or_else(|| "agileplus-mcp-intent".to_string());
    let now = Utc::now().to_rfc3339();

    let intent_title = extract_intent_title(&req.prompt);
    let intent_slug = slugify(&intent_title);
    let intent_id = format!("Intent#{}", intent_slug);

    let features = decompose_features(&req.prompt);

    let builder = IntentGraphBuilder::new().with_default_metadata();
    let (builder, plan_id, feature_nodes, test_nodes) = build_nodes(
        builder,
        &intent_id,
        &intent_slug,
        &intent_title,
        &features,
        &req.prompt,
        &source,
        &agent,
        &now,
    );

    let builder = wire_edges(
        builder,
        &intent_id,
        &plan_id,
        &feature_nodes,
        &test_nodes,
        &source,
        &agent,
        &now,
    );

    let graph = builder.build().map_err(|e| anyhow::anyhow!(e))?;

    let confidence = compute_confidence(&graph);

    if let Err(errs) = validate_intent_graph(&graph) {
        let messages: Vec<String> = errs.iter().map(|e| e.to_string()).collect();
        return Err(anyhow::anyhow!(
            "Graph validation failed: {}",
            messages.join("; ")
        ));
    }

    let elapsed = start.elapsed().as_millis() as u64;

    Ok(ConvertResponse {
        graph,
        confidence,
        tokens_used: None,
        processing_time_ms: elapsed,
    })
}

#[allow(clippy::too_many_arguments)]
fn build_nodes(
    mut builder: IntentGraphBuilder,
    intent_id: &str,
    intent_slug: &str,
    intent_title: &str,
    features: &[String],
    prompt: &str,
    source: &str,
    agent: &str,
    now: &str,
) -> (IntentGraphBuilder, String, Vec<Node>, Vec<Node>) {
    // Intent
    let intent_meta = MetaBuilder::new()
        .source(source)
        .timestamp(now)
        .agent_id(agent)
        .confidence(0.95)
        .build()
        .expect("valid intent meta");
    let intent_node = NodeBuilder::new()
        .id(intent_id)
        .node_type(NodeType::Intent)
        .dag_stage(DagStage::Intent)
        .title(intent_title)
        .description(prompt)
        .status(Status::Active)
        .meta(intent_meta)
        .build()
        .expect("valid intent node");
    builder = builder.node(intent_node);

    // Plan
    let plan_id = format!("Plan#{}", intent_slug);
    let plan_meta = MetaBuilder::new()
        .source(source)
        .timestamp(now)
        .agent_id(agent)
        .confidence(0.90)
        .build()
        .expect("valid plan meta");
    let plan_node = NodeBuilder::new()
        .id(&plan_id)
        .node_type(NodeType::Plan)
        .dag_stage(DagStage::Plan)
        .title(format!("Plan for {}", intent_title))
        .status(Status::Draft)
        .meta(plan_meta)
        .build()
        .expect("valid plan node");
    builder = builder.node(plan_node);

    // Features
    let mut feature_nodes = Vec::new();
    for (idx, feat_title) in features.iter().enumerate() {
        let feat_id = format!("Feature#{}-sub{}", intent_slug, idx + 1);
        let meta = MetaBuilder::new()
            .source(source)
            .timestamp(now)
            .agent_id(agent)
            .confidence(0.85)
            .build()
            .expect("valid feature meta");
        let node = NodeBuilder::new()
            .id(&feat_id)
            .node_type(NodeType::Feature)
            .dag_stage(DagStage::Feature)
            .title(feat_title)
            .status(Status::Draft)
            .meta(meta)
            .build()
            .expect("valid feature node");
        feature_nodes.push(node.clone());
        builder = builder.node(node);
    }

    // Tests
    let mut test_nodes = Vec::new();
    for (idx, feat) in feature_nodes.iter().enumerate() {
        let test_id = format!("Test#{}-sub{}-test", intent_slug, idx + 1);
        let meta = MetaBuilder::new()
            .source(source)
            .timestamp(now)
            .agent_id(agent)
            .confidence(0.80)
            .build()
            .expect("valid test meta");
        let node = NodeBuilder::new()
            .id(&test_id)
            .node_type(NodeType::Test)
            .dag_stage(DagStage::Test)
            .title(format!("Tests for {}", feat.title))
            .status(Status::Draft)
            .meta(meta)
            .build()
            .expect("valid test node");
        test_nodes.push(node.clone());
        builder = builder.node(node);
    }

    (builder, plan_id, feature_nodes, test_nodes)
}

#[allow(clippy::too_many_arguments)]
fn wire_edges(
    mut builder: IntentGraphBuilder,
    intent_id: &str,
    plan_id: &str,
    feature_nodes: &[Node],
    test_nodes: &[Node],
    source: &str,
    agent: &str,
    now: &str,
) -> IntentGraphBuilder {
    // Intent -> Plan
    builder = add_edge(
        builder,
        intent_id,
        plan_id,
        RelationshipType::TracesTo,
        CanonicalLinkType::ParentOf,
        Direction::Forward,
        0.90,
        source,
        agent,
        now,
    );

    // Intent -> Features (implements)
    for feat in feature_nodes {
        builder = add_edge(
            builder,
            intent_id,
            &feat.id.0,
            RelationshipType::Implements,
            CanonicalLinkType::ParentOf,
            Direction::Forward,
            0.80,
            source,
            agent,
            now,
        );
    }

    // Plan -> Features (traces-to)
    for feat in feature_nodes {
        builder = add_edge(
            builder,
            plan_id,
            &feat.id.0,
            RelationshipType::TracesTo,
            CanonicalLinkType::ParentOf,
            Direction::Forward,
            0.75,
            source,
            agent,
            now,
        );
    }

    // Feature -> Test (tests)
    for (idx, feat) in feature_nodes.iter().enumerate() {
        let test_node = &test_nodes[idx];
        builder = add_edge(
            builder,
            &feat.id.0,
            &test_node.id.0,
            RelationshipType::Tests,
            CanonicalLinkType::Verifies,
            Direction::Forward,
            0.70,
            source,
            agent,
            now,
        );
    }

    builder
}

#[allow(clippy::too_many_arguments)]
fn add_edge(
    builder: IntentGraphBuilder,
    source: &str,
    target: &str,
    rel: RelationshipType,
    link: CanonicalLinkType,
    dir: Direction,
    confidence: f32,
    src: &str,
    agent: &str,
    now: &str,
) -> IntentGraphBuilder {
    let meta = MetaBuilder::new()
        .source(src)
        .timestamp(now)
        .agent_id(agent)
        .confidence(confidence as f64)
        .build()
        .expect("valid edge meta");
    let edge = EdgeBuilder::new()
        .auto_id()
        .source(source)
        .target(target)
        .relationship_type(rel)
        .canonical_map(
            CanonicalMapBuilder::new()
                .link_type(link)
                .direction(dir)
                .build(),
        )
        .meta(meta)
        .build()
        .expect("valid edge");
    builder.edge(edge)
}
