use super::super::framework::{EvalImpl, eval_input};
use crate::gui_builder::node_registry::{NodeSpec, ParamKind};
use crate::gui_builder::nodes::{CastTarget, Node, NodeCategory, NodeGraph, NodeLibrary, NodeParams, Pin, PinType};
use crate::gui_builder::runtime::{RuntimeContext, RuntimeValue};
use num_bigint::BigInt;
use num_traits::ToPrimitive;

pub struct TypeCastNode;

impl EvalImpl for TypeCastNode {
    fn evaluate(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, _pin_id: usize) -> Result<RuntimeValue, String> {
        // Get the target type from params
        let target = if let NodeParams::TypeCast(_, output) = &node.params {
            *output
        } else {
            CastTarget::String // Default to string
        };

        // Get the input value
        let input = eval_input(graph, ctx, node.id, "input")?;

        // Convert to target type
        let result = match target {
            CastTarget::String => {
                RuntimeValue::String(match &input {
                    RuntimeValue::String(s) => s.clone(),
                    RuntimeValue::Float(f) => f.to_string(),
                    RuntimeValue::Bool(b) => b.to_string(),
                    RuntimeValue::BigInt(bi) => bi.to_string(),
                    RuntimeValue::Color(c) => format!("rgba({}, {}, {}, {})", c[0], c[1], c[2], c[3]),
                    RuntimeValue::Array(arr) => format!("[{} elements]", arr.len()),
                    RuntimeValue::Map(map) => format!("{{{}  entries}}", map.len()),
                    RuntimeValue::Tuple(tuple) => format!("({} elements)", tuple.len()),
                    RuntimeValue::None => String::new(),
                })
            }
            CastTarget::Float => {
                RuntimeValue::Float(match &input {
                    RuntimeValue::String(s) => s.parse().unwrap_or(0.0),
                    RuntimeValue::Float(f) => *f,
                    RuntimeValue::Bool(b) => if *b { 1.0 } else { 0.0 },
                    RuntimeValue::BigInt(bi) => bi.to_f32().unwrap_or(0.0),
                    RuntimeValue::Color(c) => c[0], // Return red channel
                    RuntimeValue::Array(arr) => arr.len() as f32, // Length as float
                    RuntimeValue::Map(map) => map.len() as f32,
                    RuntimeValue::Tuple(tuple) => tuple.len() as f32,
                    RuntimeValue::None => 0.0,
                })
            }
            CastTarget::Bool => {
                RuntimeValue::Bool(match &input {
                    RuntimeValue::String(s) => !s.is_empty() && s != "false" && s != "0",
                    RuntimeValue::Float(f) => *f != 0.0,
                    RuntimeValue::Bool(b) => *b,
                    RuntimeValue::BigInt(bi) => *bi != BigInt::from(0),
                    RuntimeValue::Color(c) => c[3] > 0.0, // Check alpha
                    RuntimeValue::Array(arr) => !arr.is_empty(), // Non-empty is true
                    RuntimeValue::Map(map) => !map.is_empty(),
                    RuntimeValue::Tuple(tuple) => !tuple.is_empty(),
                    RuntimeValue::None => false,
                })
            }
            CastTarget::BigInt => {
                RuntimeValue::BigInt(match &input {
                    RuntimeValue::String(s) => s.parse().unwrap_or_else(|_| BigInt::from(0)),
                    RuntimeValue::Float(f) => BigInt::from(*f as i64),
                    RuntimeValue::Bool(b) => if *b { BigInt::from(1) } else { BigInt::from(0) },
                    RuntimeValue::BigInt(bi) => bi.clone(),
                    RuntimeValue::Color(_) => BigInt::from(0),
                    RuntimeValue::Array(arr) => BigInt::from(arr.len() as i64), // Length
                    RuntimeValue::Map(map) => BigInt::from(map.len() as i64),
                    RuntimeValue::Tuple(tuple) => BigInt::from(tuple.len() as i64),
                    RuntimeValue::None => BigInt::from(0),
                })
            }
        };

        Ok(result)
    }
}

impl super::super::framework::NodeDescription for TypeCastNode {
    fn description(&self) -> &str {
        "Converts a value from one type to another (e.g., Number to String)."
    }
}

fn default_params() -> NodeParams {
    NodeParams::TypeCast(CastTarget::String, CastTarget::String)
}

fn pins(next_pin_id: &mut usize) -> Vec<Pin> {
    let mut pins = Vec::new();
    pins.push(Pin::new(*next_pin_id, PinType::Any, true, "input"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::Any, false, "output"));
    *next_pin_id += 1;
    pins
}

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

crate::register_node!(NodeSpec {
    id: "TypeCast",
    display_name: "Type Cast",
    documentation: "Converts a value from one type to another (e.g., Number to String).",
    category: NodeCategory::Data,
    library: NodeLibrary::Core,
    param_kind: ParamKind::TypeCast,
    default_params,
    pins,
    execute: None,
    evaluate: Some(evaluate),
});
