use super::super::framework::{EvalImpl, NodeImpl, eval_input, get_default_exec_out};
use crate::gui_builder::node_registry::{NodeSpec, ParamKind};
use crate::gui_builder::nodes::{Node, NodeCategory, NodeGraph, NodeLibrary, NodeParams, Pin, PinType};
use crate::gui_builder::interpreter::ExecutionStack;
use crate::gui_builder::runtime::{RuntimeContext, RuntimeValue};

pub struct SetVariableNode;

impl NodeImpl for SetVariableNode {
    fn execute(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node) -> Result<Option<usize>, String> {
        let var_name = match &node.params {
            NodeParams::VariableName(s) => s,
            _ => return Err("Missing variable name".to_string()),
        };
        
        let value = eval_input(graph, ctx, node.id, "value")?;
        ctx.set_variable(var_name.clone(), value);
        
        get_default_exec_out(node)
    }
}

// Allow getting the value out for chaining
impl super::super::framework::EvalImpl for SetVariableNode {
    fn evaluate(&self, _graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, _pin_id: usize) -> Result<RuntimeValue, String> {
        let var_name = match &node.params {
            NodeParams::VariableName(s) => s,
            _ => return Err("Missing variable name".to_string()),
        };
        
        // Return the current value of the variable (which should have been set during execute)
        let val = ctx.get_variable(var_name).unwrap_or(RuntimeValue::String(String::new()));
        Ok(val)
    }
}

impl super::super::framework::NodeDescription for SetVariableNode {
    fn description(&self) -> &str {
        "Sets a new value for a global variable."
    }
}

fn default_params() -> NodeParams {
    NodeParams::VariableName(String::new())
}

fn pins(next_pin_id: &mut usize) -> Vec<Pin> {
    let mut pins = Vec::new();
    // Exec in/out
    pins.push(Pin::new(*next_pin_id, PinType::Exec, true, ""));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Exec, false, ""));
    *next_pin_id += 1;
    // Value in
    pins.push(Pin::new(*next_pin_id, PinType::Any, true, "value"));
    *next_pin_id += 1;
    // Value out (chaining)
    pins.push(Pin::new(*next_pin_id, PinType::Any, false, "out"));
    *next_pin_id += 1;
    pins
}

fn exec(graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, _stack: &mut ExecutionStack) -> Result<Option<usize>, String> {
    SetVariableNode.execute(graph, ctx, node)
}

fn eval(graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, pin_id: usize) -> Result<RuntimeValue, String> {
    SetVariableNode.evaluate(graph, ctx, node, pin_id)
}

crate::register_node!(NodeSpec {
    id: "SetVariable",
    display_name: "Set Variable",
    documentation: "Sets a new value for a global variable.",
    category: NodeCategory::Action,
    library: NodeLibrary::Core,
    param_kind: ParamKind::VariableSelector,
    default_params,
    pins,
    execute: Some(exec),
    evaluate: Some(eval),
});
