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

pub struct BigIntOpNode;

impl EvalImpl for BigIntOpNode {
    fn evaluate(&self, graph: &NodeGraph, ctx: &mut RuntimeContext, node: &Node, _pin_id: usize) -> Result<RuntimeValue, String> {
        let a = eval_input(graph, ctx, node.id, "a")?.as_bigint();
        let b = eval_input(graph, ctx, node.id, "b")?.as_bigint();

        let op = match &node.params {
            NodeParams::BigIntOperation(op) => op,
            _ => &BigIntOp::Add,
        };

        let result = match op {
            BigIntOp::Add => a + b,
            BigIntOp::Subtract => a - b,
            BigIntOp::Multiply => a * b,
            BigIntOp::Divide => {
                if b.is_zero() {
                    return Err("Division by zero".to_string());
                }
                a / b
            }
            BigIntOp::Modulo => {
                if b.is_zero() {
                    return Err("Modulo by zero".to_string());
                }
                a % b
            }
            BigIntOp::Power => {
                // Convert b to u32 for exponentiation (reasonable limit)
                let exp = b.to_string().parse::<u32>().unwrap_or(0);
                if exp > 100000 {
                    return Err(format!("Exponent {} is too large (max 100000)", exp));
                }
                num_traits::pow(a, exp as usize)
            }
        };

        Ok(RuntimeValue::BigInt(result))
    }
}

impl super::super::framework::NodeDescription for BigIntOpNode {
    fn description(&self) -> &str {
        "Performs arithmetic operations on arbitrary-precision integers (Add, Subtract, Multiply, Divide, Modulo, Power)."
    }
}

fn default_params() -> NodeParams {
    NodeParams::BigIntOperation(BigIntOp::Add)
}

fn pins(next_pin_id: &mut usize) -> Vec<Pin> {
    let mut pins = Vec::new();
    pins.push(Pin::new(*next_pin_id, PinType::BigInt, true, "a"));
    *next_pin_id += 1;
    pins.push(Pin::new(*next_pin_id, PinType::BigInt, true, "b"));
    *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> {
    BigIntOpNode.evaluate(graph, ctx, node, pin_id)
}

crate::register_node!(NodeSpec {
    id: "BigIntOp",
    display_name: "BigInt Math",
    documentation: "Performs arithmetic operations on arbitrary-precision integers (Add, Subtract, Multiply, Divide, Modulo, Power).",
    category: NodeCategory::Data,
    library: NodeLibrary::Core,
    param_kind: ParamKind::BigIntOperation,
    default_params,
    pins,
    execute: None,
    evaluate: Some(evaluate),
});
