use crate::gui_builder::nodes::{Node, Pin, PinType, NodeGraph};
use crate::gui_builder::runtime::{RuntimeContext, RuntimeValue};

/// Trait that all modular nodes must implement
pub trait NodeImpl {
    /// Execute the node logic (for Flow nodes)
    /// Returns: Ok(Some(pin_id)) to continue flow, Ok(None) to stop flow, Err(msg) on failure
    fn execute(&self, _graph: &NodeGraph, _ctx: &mut RuntimeContext, _node: &Node) -> Result<Option<usize>, String> {
        Ok(None)
    }
}

/// Trait for nodes that return values (Data nodes)
pub trait EvalImpl {
    /// Evaluate the node output
    fn evaluate(&self, _graph: &NodeGraph, _ctx: &mut RuntimeContext, _node: &Node, _pin_id: usize) -> Result<RuntimeValue, String> {
        Ok(RuntimeValue::None)
    }
}

/// Trait for modular documentation
pub trait NodeDescription {
    /// Get the user-friendly description of the node
    fn description(&self) -> &str {
        "No description available."
    }
}

/// Helper to get the default execution output pin (unnamed exec output)
pub fn get_default_exec_out(node: &Node) -> Result<Option<usize>, String> {
    Ok(node.pins.iter()
        .find(|p| !p.is_input && p.pin_type == PinType::Exec && p.name.is_empty())
        .map(|p| p.id))
}

pub fn eval_input(graph: &NodeGraph, ctx: &mut RuntimeContext, node_id: usize, pin_name: &str) -> Result<RuntimeValue, String> {
    let node = graph.get_node(node_id).ok_or("Node not found")?;
    let input_pin = node.pins.iter().find(|p| p.is_input && p.name == pin_name)
        .ok_or_else(|| format!("Pin {} not found", pin_name))?;
    let connection = graph.connections.iter().find(|c| c.to_node == node_id && c.to_pin == input_pin.id);
        
    if let Some(conn) = connection {
        crate::gui_builder::interpreter::eval_node_output(graph, ctx, conn.from_node, conn.from_pin)
    } else {
        Ok(RuntimeValue::None)
    }
}

pub fn eval_input_string(graph: &NodeGraph, ctx: &mut RuntimeContext, node_id: usize, pin_name: &str) -> Result<String, String> {
    let val = eval_input(graph, ctx, node_id, pin_name)?;
    Ok(val.as_string())
}

pub fn eval_input_float(graph: &NodeGraph, ctx: &mut RuntimeContext, node_id: usize, pin_name: &str) -> Result<f64, String> {
    let val = eval_input(graph, ctx, node_id, pin_name)?;
    Ok(val.as_float() as f64)
}
