use crate::parser::types::{Value, FunctionValue};
use crate::parser::interpreter::{Interpreter, DynamicFn, DynamicFnInfo};
use std::sync::{Arc, Mutex};

fn check_rec(schema: &Value, val: &Value) -> bool {
    match schema {
        // Primitive type by string
        Value::SingleString(tname) => match tname.as_str() {
            "int" => matches!(val, Value::Int(_)),
            "long" => matches!(val, Value::Long(_)),
            "float" => matches!(val, Value::Float(_)),
            "bool" => matches!(val, Value::Bool(_)),
            "string" => matches!(val, Value::SingleString(_)),
            "int_array" => matches!(val, Value::IntArray(_)),
            "float_array" => matches!(val, Value::FloatArray(_)),
            "str_array" => matches!(val, Value::StrArray(_)),
            "mixed_array" => matches!(val, Value::MixedArray(_)),
            _ => false,
        },
        // Array of primitives
        Value::StrArray(schema_arr) if schema_arr.len() == 1 => match schema_arr[0].as_str() {
            "int" => matches!(val, Value::IntArray(_)),
            "float" => matches!(val, Value::FloatArray(_)),
            "string" => matches!(val, Value::StrArray(_)),
            "mixed" => matches!(val, Value::MixedArray(_)),
            _ => false,
        },
        // Keyed array (object) schema
        Value::KeyedArray(schema_map) => {
            if let Value::KeyedArray(val_map) = val {
                for (k, sch_v) in schema_map {
                    if let Some(val_v) = val_map.get(k) {
                        if !check_rec(sch_v, val_v) {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
                true
            } else {
                false
            }
        }
        // Array of objects: schema = MixedArray([object_schema]), value = MixedArray([...])
        Value::MixedArray(schema_items) if schema_items.len() == 1 => {
            let item_schema = &schema_items[0];
            if let Value::MixedArray(vals) = val {
                for item in vals {
                    if !check_rec(item_schema, item) {
                        return false;
                    }
                }
                true
            } else {
                false
            }
        }
        _ => false,
    }
}

fn check_bridge(
    _interp: &mut Interpreter,
    args: Vec<Value>
) -> Result<Value, String> {
    if args.len() != 2 {
        return Err("check(schema, value) expects 2 arguments".into());
    }
    let schema = &args[0];
    let value = &args[1];
    let ok = check_rec(schema, value);
    Ok(Value::Bool(ok))
}

pub fn register_check(interp: &mut Interpreter) {
    let f: DynamicFn = Arc::new(Mutex::new(check_bridge));
    let info = DynamicFnInfo::new(f, true);
    interp.register_dynamic_function_ex("check", info);
    interp.set_variable(
        "check",
        Value::Function(Box::new(FunctionValue::Named("check".to_string())))
    );
}
