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::{Node, NodeCategory, NodeGraph, NodeLibrary, NodeParams, Pin, PinType};
use crate::gui_builder::interpreter::ExecutionStack;
use crate::gui_builder::runtime::{RuntimeContext, RuntimeValue};
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};

pub struct FileWriteNode;

/// Expand Windows environment variables in a path (e.g., %HOMEDRIVE%, %HOMEPATH%, %USERPROFILE%)
fn expand_env_vars(path: &str) -> String {
    let mut result = path.to_string();
    // Find all %VAR% patterns and replace with env var values
    while let Some(start) = result.find('%') {
        if let Some(end) = result[start + 1..].find('%') {
            let var_name = &result[start + 1..start + 1 + end];
            let replacement = std::env::var(var_name).unwrap_or_default();
            result = format!("{}{}{}", &result[..start], replacement, &result[start + 2 + end..]);
        } else {
            break;
        }
    }
    result
}

impl NodeImpl for FileWriteNode {
    fn execute(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node) -> Result<Option<usize>, String> {
        let path_raw = eval_input_string(graph, ctx, node.id, "path")?;
        let path = expand_env_vars(&path_raw); // Expand environment variables
        let content = eval_input_string(graph, ctx, node.id, "content")?;
        
        if let Some(parent) = std::path::Path::new(&path).parent() {
            let _ = std::fs::create_dir_all(parent);
        }

        // Check if content is base64-encoded binary data
        let write_result = if content.starts_with("base64:") {
            // Decode base64 and write as bytes
            let base64_data = &content[7..]; // Skip "base64:" prefix
            match BASE64.decode(base64_data) {
                Ok(bytes) => std::fs::write(&path, bytes),
                Err(e) => return Err(format!("Base64 decode error: {}", e)),
            }
        } else {
            // Write as text
            std::fs::write(&path, &content)
        };

        match write_result {
            Ok(_) => {
                ctx.store_temp_value(node.id, "error_msg", RuntimeValue::String(String::new()));
                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) => {
                ctx.store_temp_value(node.id, "error_msg", RuntimeValue::String(e.to_string()));
                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 FileWriteNode {
    fn description(&self) -> &str {
        "Writes content to a file, overwriting existing content."
    }
}

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::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, "path"));
    *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, "error_msg"));
    *next_pin_id += 1;
    pins
}

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

crate::register_node!(NodeSpec {
    id: "FileWrite",
    display_name: "File Write",
    documentation: "Writes content to a file, overwriting existing content. Supports base64-encoded binary data with 'base64:' prefix.",
    category: NodeCategory::FileSystem,
    library: NodeLibrary::FileSystem,
    param_kind: ParamKind::None,
    default_params,
    pins,
    execute: Some(execute),
    evaluate: None,
});
