use std::rc::Rc;

use crate::{
    error::{MachineError, ValueConversionError},
    vm::{
        env::Environment,
        value::convert::{AnyFunction, TryFromValue},
    },
};

pub fn load(env: &Rc<Environment>) {
    // env.defun_native("map", |vm, env, args| {
    //     let [f, xs] = args else {
    //         return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
    //     };
    //     let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
    //     let xs = xs.proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument));
    //     let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, env, slice::from_ref(v?))))?;
    //     Ok(out)
    // });
    // env.defun_native("filter", |vm, env, args| {
    //     let [f, xs] = args else {
    //         return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
    //     };
    //     let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
    //     let xs = xs
    //         .proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument))
    //         .map(|x| x.cloned());
    //     let out = Value::try_list_or_nil(xs.try_filter(|v| {
    //         let result = f.invoke(vm, env, slice::from_ref(v))?;
    //         Ok(bool::try_from_value(&result).unwrap_or_default())
    //     }))?;
    //     Ok(out)
    // });
    env.defun_native(
        "apply",
        "Applies the function to a given argument list",
        |vm, env, args| {
            let [f, xs] = args else {
                return Err(MachineError::InvalidArgumentCount);
            };
            let f = AnyFunction::try_from_value(f)?;
            let args = xs
                .proper_iter(MachineError::ValueConversion(ValueConversionError {
                    expected: "proper list".into(),
                    got: xs.clone(),
                }))
                .map(|x| x.cloned())
                .collect::<Result<Vec<_>, _>>()?;
            f.invoke(vm, env, &args[..])
        },
    );
    env.defun_native("identity", "Returns the argument as is", |_, _, args| {
        let [arg] = args else {
            return Err(MachineError::InvalidArgumentCount);
        };
        Ok(arg.clone())
    });
}
