use super::super::framework::{EvalImpl, eval_input};
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};
use num_bigint::BigInt;
use num_traits::{One, Zero};

pub struct FactorialNode;

impl FactorialNode {
    /// Calculate factorial using BigInt for arbitrary precision
    fn factorial(n: u64) -> BigInt {
        if n == 0 || n == 1 {
            return BigInt::one();
        }
        
        let mut result = BigInt::one();
        for i in 2..=n {
            result *= i;
        }
        result
    }
}

impl EvalImpl for FactorialNode {
    fn evaluate(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, _pin_id: usize) -> Result<RuntimeValue, String> {
        // Get the input value (can be Float or BigInt)
        let input = eval_input(graph, ctx, node.id, "n")?;
        
        // Convert to u64 for factorial calculation
        let n = match &input {
            RuntimeValue::Float(f) => {
                if *f < 0.0 {
                    return Err("Factorial is not defined for negative numbers".to_string());
                }
                *f as u64
            }
            RuntimeValue::BigInt(bi) => {
                if *bi < BigInt::zero() {
                    return Err("Factorial is not defined for negative numbers".to_string());
                }
                // Convert BigInt to u64, capping at a reasonable max
                bi.to_string().parse::<u64>().unwrap_or(0)
            }
            RuntimeValue::String(s) => {
                s.parse::<u64>().unwrap_or(0)
            }
            _ => 0,
        };
        
        // Limit factorial input to prevent extremely long calculations
        // 1000! has about 2568 digits, which is reasonable
        if n > 10000 {
            return Err(format!("Factorial input {} is too large (max 10000)", n));
        }
        
        let result = Self::factorial(n);
        Ok(RuntimeValue::BigInt(result))
    }
}

impl super::super::framework::NodeDescription for FactorialNode {
    fn description(&self) -> &str {
        "Calculates the factorial of a number."
    }
}

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::Float, true, "n"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::BigInt, false, "result"));
    *next_pin_id += 1;
    pins
}

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

crate::register_node!(NodeSpec {
    id: "Factorial",
    display_name: "Factorial (Deprecated)",
    documentation: "DEPRECATED: Calculates factorial. Use ForLoop + BigIntOp(Multiply) instead for more flexibility.",
    category: NodeCategory::Data,
    library: NodeLibrary::Core,
    param_kind: ParamKind::None,
    default_params,
    pins,
    execute: None,
    evaluate: Some(evaluate),
});
