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

pub struct CreateFileNode;

impl NodeImpl for CreateFileNode {
    fn execute(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node) -> Result<Option<usize>, String> {
        // Resolve inputs
        let name = eval_input_string(graph, ctx, node.id, "name")?;
        let extension = eval_input_string(graph, ctx, node.id, "extension")?;
        let content = eval_input_string(graph, ctx, node.id, "content")?;
        
        let location = if let NodeParams::FileLocation(loc) = &node.params { 
            loc 
        } else { 
            &FileLocation::Documents 
        };

        // Resolve Directory
        let mut dir_path = match location {
            FileLocation::Desktop => dirs::desktop_dir(),
            FileLocation::Documents => dirs::document_dir(),
            FileLocation::Downloads => dirs::download_dir(),
            FileLocation::Home => dirs::home_dir(),
            FileLocation::Temp => Some(std::env::temp_dir()),
        }.ok_or("Could not determine directory path")?;

        // Construct Filename
        let filename = if extension.is_empty() {
            name.clone()
        } else if name.ends_with(&format!(".{}", extension)) {
             name.clone()
        } else {
            format!("{}.{}", name, extension)
        };
        
        dir_path.push(filename);
        let full_path = dir_path.to_string_lossy().to_string();

        // Perform Write
        match std::fs::write(&full_path, content) {
             Ok(_) => {
                let metadata = std::fs::metadata(&full_path).map_err(|e| e.to_string())?;
                let size = metadata.len() as f32;

                // Set Outputs
                ctx.store_temp_value(node.id, "full_path", RuntimeValue::String(full_path));
                ctx.store_temp_value(node.id, "size", RuntimeValue::Float(size));
                ctx.store_temp_value(node.id, "error_msg", RuntimeValue::String(String::new()));
                
                // Return 'success' pin
                let pin_id = node.pins.iter()
                    .find(|p| !p.is_input && p.name == "success" && p.pin_type == PinType::Exec)
                    .map(|p| p.id);
                Ok(pin_id)
             }
             Err(e) => {
                // Set Outputs (Error)
                ctx.store_temp_value(node.id, "full_path", RuntimeValue::String(String::new()));
                ctx.store_temp_value(node.id, "size", RuntimeValue::Float(0.0));
                ctx.store_temp_value(node.id, "error_msg", RuntimeValue::String(e.to_string()));
                
                // Return 'error' pin
                let pin_id = node.pins.iter()
                    .find(|p| !p.is_input && p.name == "error" && p.pin_type == PinType::Exec)
                    .map(|p| p.id);
                Ok(pin_id)
             }
        }
    }
}

impl super::super::framework::NodeDescription for CreateFileNode {
    fn description(&self) -> &str {
        "Creates a new empty file."
    }
}

fn default_params() -> NodeParams {
    NodeParams::FileLocation(FileLocation::Documents)
}

fn pins(next_pin_id: &mut usize) -> Vec<Pin> {
    let mut pins = Vec::new();
    pins.push(Pin::new(*next_pin_id, PinType::Exec, true, ""));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Exec, false, "success"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Exec, false, "error"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, true, "name"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, true, "extension"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, true, "content"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, false, "full_path"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Float, false, "size"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::String, false, "error_msg"));
    *next_pin_id += 1;
    pins
}

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

crate::register_node!(NodeSpec {
    id: "CreateFile",
    display_name: "Create File",
    documentation: "Creates a new file at a specified location (Desktop, Documents, Downloads, Home, or Temp).",
    category: NodeCategory::FileSystem,
    library: NodeLibrary::FileSystem,
    param_kind: ParamKind::None,
    default_params,
    pins,
    execute: Some(execute),
    evaluate: None,
});
