use std::rc::Rc;

use crate::{
    error::MachineError,
    vm::{
        Value,
        env::Environment,
        value::{NumberValue, StringValue, convert::TryFromValue},
    },
};

pub fn load(env: &Rc<Environment>) {
    env.defun_native(
        "symbol?",
        "Returns #t if the argument is an identifier",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            Ok(matches!(arg, Value::Identifier(_)).into())
        },
    );
    env.defun_native(
        "quote?",
        "Returns #t if the argument is a quote",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            Ok(matches!(arg, Value::Quote(_)).into())
        },
    );
    env.defun_native(
        "list?",
        "Returns #t if the argument is a list",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            Ok(matches!(arg, Value::Cons(_) | Value::Nil).into())
        },
    );
    env.defun_native(
        "cons?",
        "Returns #t if the argument is a cons pair",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            Ok(matches!(arg, Value::Cons(_)).into())
        },
    );
    env.defun_native(
        "number?",
        "Returns #t if the argument is a number",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            Ok(matches!(arg, Value::Number(_)).into())
        },
    );
    env.defun_native(
        "string?",
        "Returns #t if the argument is a string",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            Ok(matches!(arg, Value::String(_)).into())
        },
    );
    env.defun_native("nil?", "Returns #t if the argument is nil", |_, _, args| {
        let [arg] = args else {
            return Err(MachineError::InvalidArgumentCount);
        };
        Ok(matches!(arg, Value::Nil).into())
    });

    env.defun_native(
        "symbol",
        "Constructs a symbol from a string",
        |_, _, args| {
            let [string] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            let string = StringValue::try_from_value(string)?;
            Ok(Value::Identifier((&*string).into()))
        },
    );
    env.defun_native(
        "->string",
        "Converts a value to string representation",
        |_, _, args| {
            let [arg] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            match arg {
                Value::String(_) => Ok(arg.clone()),
                _ => Ok(Value::String(format!("{arg}").into())),
            }
        },
    );
    env.defun_native(
        "string->number",
        "Converts a string to a number with optional radix (default=10)",
        |_, _, args| {
            let (string, radix) = match args {
                [string] => (StringValue::try_from_value(string)?, 10),
                [string, radix] => (
                    StringValue::try_from_value(string)?,
                    i64::try_from_value(radix)?,
                ),
                _ => return Err(MachineError::InvalidArgumentCount),
            };
            let Ok(radix) = u32::try_from(radix) else {
                return Ok(Value::Nil);
            };

            match NumberValue::from_str_radix(string.as_ref(), radix) {
                Some(value) => Ok(Value::Number(value)),
                None => Ok(Value::Nil),
            }
        },
    );

    // // conversion
    // env.defun_native("string->int", |vm, _, args| {
    //     let [arg] = args else {
    //         return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
    //     };
    //     let arg = ValueString::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
    //     let result = arg.parse::<i64>().map(Value::Integer).unwrap_or(Value::Nil);
    //     Ok(result)
    // });
    // env.defun_native("int->string", |vm, _, args| {
    //     let [arg] = args else {
    //         return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
    //     };
    //     let arg = i64::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
    //     let result = Value::String(format!("{arg}").into());
    //     Ok(result)
    // });
    // env.defun_native("type", |vm, _, args| {
    //     let [arg] = args else {
    //         return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
    //     };
    //     Ok(arg.typeid())
    // });
}
