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

pub struct JsonExtractArrayNode;

impl EvalImpl for JsonExtractArrayNode {
    fn evaluate(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, pin_id: usize) -> Result<RuntimeValue, String> {
        let json_str = eval_input_string(graph, ctx, node.id, "json")?;
        let path = eval_input_string(graph, ctx, node.id, "path")?;
        let field = eval_input_string(graph, ctx, node.id, "field")?;
        
        // Parse JSON
        let json: serde_json::Value = serde_json::from_str(&json_str)
            .map_err(|e| format!("JSON parse error: {}", e))?;
        
        // Navigate to path
        let mut current = &json;
        if !path.is_empty() {
            for part in path.split('.') {
                current = current.get(part).unwrap_or(&serde_json::Value::Null);
            }
        }
        
        // Extract array and optionally extract field from each element
        let items: Vec<RuntimeValue> = if let Some(arr) = current.as_array() {
            arr.iter().filter_map(|item| {
                if field.is_empty() {
                    // Return whole item as string
                    Some(RuntimeValue::String(item.to_string()))
                } else {
                    // Extract specific field
                    item.get(&field).map(|v| {
                        if let Some(s) = v.as_str() {
                            RuntimeValue::String(s.to_string())
                        } else {
                            RuntimeValue::String(v.to_string())
                        }
                    })
                }
            }).collect()
        } else {
            Vec::new()
        };
        
        let count = items.len();
        
        // Find output pin name to determine which value to return
        if let Some(pin) = node.pins.iter().find(|p| p.id == pin_id) {
            match pin.name.as_str() {
                "count" => Ok(RuntimeValue::Float(count as f32)),
                _ => Ok(RuntimeValue::Array(items)),
            }
        } else {
            Ok(RuntimeValue::Array(items))
        }
    }
}

impl super::super::framework::NodeDescription for JsonExtractArrayNode {
    fn description(&self) -> &str {
        "Extracts an array from JSON at a specified path and returns it as an Array type."
    }
}

fn default_params() -> NodeParams {
    NodeParams::None
}

fn pins(next_pin_id: &mut usize) -> Vec<Pin> {
    let mut pins = Vec::new();
    pins.push(Pin::new(*next_pin_id, PinType::String, true, "json"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, true, "path"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, true, "field"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Array, false, "array"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Float, false, "count"));
    *next_pin_id += 1;
    pins
}

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

crate::register_node!(NodeSpec {
    id: "JsonExtractArray",
    display_name: "JSON Extract Array",
    documentation: "Extracts an array from JSON at a specified path and returns it as an Array type.",
    category: NodeCategory::Data,
    library: NodeLibrary::Json,
    param_kind: ParamKind::None,
    default_params,
    pins,
    execute: None,
    evaluate: Some(eval),
});
