// FILE: src/parser/interpreter/integration.rs

use crate::parser::interpreter::Interpreter;
use crate::parser::types::{
    ComposeMode,
    FunctionValue,
    Value,
    InkIteratorKind,
    FunctionValue::{
        Named,
        InlineLambdaAST,
        InlineLambdaASTPartial,
        CurriedMap,
        CurriedMapPlaceholder,
        CurriedFilter,
        CurriedFilterPlaceholder,
        CurriedReducePartial,
        CurriedReducePlaceholder,
        Composition,
        CompositionPartial,
        RustClosure,
        CurriedProp,
        CurriedAssoc,
    },
};

fn is_placeholder(val: &Value) -> bool {
    match val {
        Value::Placeholder => true,
        Value::SingleString(s) if s == "_" => true,
        Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_" => true,
        _ => false,
    }
}

pub fn apply_one_function_value(
    interp: &mut Interpreter,
    func_val: Box<FunctionValue>,
    data: Value,
) -> Result<Value, String> {
    apply_n_ary_function_value(interp, func_val, vec![data])
}

pub fn apply_n_ary_function_value(
    interp: &mut Interpreter,
    func_val: Box<FunctionValue>,
    args: Vec<Value>,
) -> Result<Value, String> {
    match *func_val {
        Named(ref name) => {
            if let Some(bridge) = interp.get_dynamic_function(name) {
                let closure = bridge.lock()
                    .map_err(|_| format!("Dynamic function lock error for '{}'", name))?;
                closure(interp, args)
            } else {
                Err(format!("No built-in bridging for Named('{}')", name))
            }
        }
        InlineLambdaAST { params, body_expr } => {
            let arity = params.len();
            let mut bound_args: Vec<Value> = Vec::with_capacity(arity);
            let mut n_provided = 0;
            for arg in &args {
                if n_provided >= arity {
                    return Err(format!("InlineLambdaAST => got {} extra args beyond {} params", args.len() - arity, arity));
                }
                if is_placeholder(arg) {
                    bound_args.push(Value::Placeholder);
                } else {
                    bound_args.push(arg.clone());
                }
                n_provided += 1;
            }
            // Fill remaining slots with Placeholder if not provided
            while bound_args.len() < arity {
                bound_args.push(Value::Placeholder);
            }
            if bound_args.iter().all(|v| !is_placeholder(v)) {
                call_inline_lambda_ast(interp, &params, &body_expr, bound_args)
            } else {
                Ok(Value::Function(Box::new(InlineLambdaASTPartial {
                    params,
                    body_expr,
                    bound_args,
                })))
            }
        }
        InlineLambdaASTPartial { params, body_expr, mut bound_args } => {
            let arity = params.len();
            let mut arg_idx = 0;
            for arg in &args {
                // Find next Placeholder slot in bound_args
                while arg_idx < bound_args.len() && !is_placeholder(&bound_args[arg_idx]) {
                    arg_idx += 1;
                }
                if arg_idx >= arity {
                    return Err(format!(
                        "InlineLambdaASTPartial => got {} extra args beyond {} params",
                        args.len() - (arity - arg_idx),
                        arity
                    ));
                }
                if is_placeholder(arg) {
                    bound_args[arg_idx] = Value::Placeholder;
                } else {
                    bound_args[arg_idx] = arg.clone();
                }
                arg_idx += 1;
            }
            if bound_args.iter().all(|v| !is_placeholder(v)) {
                call_inline_lambda_ast(interp, &params, &body_expr, bound_args)
            } else {
                Ok(Value::Function(Box::new(InlineLambdaASTPartial {
                    params,
                    body_expr,
                    bound_args,
                })))
            }
        }

        CurriedMap(map_fn_box) => {
            if args.is_empty() {
                Ok(Value::Function(Box::new(CurriedMapPlaceholder {
                    func: Some(map_fn_box),
                    data: None,
                })))
            } else if args.len() == 1 {
                let data_val = args.into_iter().next().unwrap();
                do_map(interp, &*map_fn_box, data_val)
            } else {
                Err(format!("array:map => expected 0 or 1 argument, got {}", args.len()))
            }
        }
        CurriedMapPlaceholder { func, data } => {
            let mut current_func = func;
            let mut current_data = data;
            for val in args {
                if current_func.is_none() {
                    match val {
                        Value::Function(fb) => current_func = Some(fb),
                        Value::Placeholder => {},
                        other => {
                            return Err(format!(
                                "CurriedMapPlaceholder => first param must be function or '_', got {:?}",
                                other
                            ));
                        }
                    }
                } else if current_data.is_none() {
                    match val {
                        Value::Placeholder => {},
                        x => current_data = Some(x),
                    }
                } else {
                    return Err("array:map => extra argument(s) after function & data".to_string());
                }
            }
            if let (Some(mf), Some(dat)) = (current_func.as_ref(), current_data.as_ref()) {
                do_map(interp, mf, dat.clone())
            } else {
                Ok(Value::Function(Box::new(CurriedMapPlaceholder {
                    func: current_func,
                    data: current_data,
                })))
            }
        }

        CurriedFilter(pred_box) => {
            if args.is_empty() {
                Ok(Value::Function(Box::new(CurriedFilterPlaceholder {
                    pred: Some(pred_box),
                    data: None,
                })))
            } else if args.len() == 1 {
                let data_val = args.into_iter().next().unwrap();
                do_filter(interp, &*pred_box, data_val)
            } else {
                Err(format!("array:filter => expected 0 or 1 arg, got {}", args.len()))
            }
        }
        CurriedFilterPlaceholder { pred, data } => {
            let mut current_pred = pred;
            let mut current_data = data;
            for val in args {
                if current_pred.is_none() {
                    match val {
                        Value::Function(fb) => current_pred = Some(fb),
                        Value::Placeholder => {},
                        other => {
                            return Err(format!(
                                "CurriedFilterPlaceholder => first param must be function or '_', got {:?}",
                                other
                            ));
                        }
                    }
                } else if current_data.is_none() {
                    match val {
                        Value::Placeholder => {},
                        v => current_data = Some(v),
                    }
                } else {
                    return Err("array:filter => extra arg(s) after function & data".to_string());
                }
            }
            if let (Some(pf), Some(dat)) = (current_pred.as_ref(), current_data.as_ref()) {
                do_filter(interp, pf, dat.clone())
            } else {
                Ok(Value::Function(Box::new(CurriedFilterPlaceholder {
                    pred: current_pred,
                    data: current_data,
                })))
            }
        }

        CurriedReducePartial(f_box, init_val) => {
            if args.is_empty() {
                Ok(Value::Function(Box::new(CurriedReducePlaceholder {
                    func: Some(f_box),
                    init_val: Some(init_val),
                    data: None,
                })))
            } else if args.len() == 1 {
                let _data_val = args.into_iter().next().unwrap();
                Err("reduce => not fully implemented".to_string())
            } else {
                Err(format!("CurriedReducePartial => expected 0 or 1 arg, got {}", args.len()))
            }
        }
        CurriedReducePlaceholder { func, init_val, data } => {
            let mut cf = func;
            let mut iv = init_val;
            let mut dv = data;
            for val in args {
                if cf.is_none() {
                    match val {
                        Value::Function(fb) => cf = Some(fb),
                        Value::Placeholder => {},
                        other => {
                            return Err(format!(
                                "array:reduce => first param must be function or '_', got {:?}",
                                other
                            ));
                        }
                    }
                } else if iv.is_none() {
                    match val {
                        Value::Placeholder => {},
                        x => iv = Some(x),
                    }
                } else if dv.is_none() {
                    match val {
                        Value::Placeholder => {},
                        x => dv = Some(x),
                    }
                } else {
                    return Err("array:reduce => extra arg(s) after function, init, data".to_string());
                }
            }
            if let (Some(_f), Some(_ini), Some(_dat)) = (cf.as_ref(), iv.as_ref(), dv.as_ref()) {
                Err("reduce => final bridging not fully implemented".to_string())
            } else {
                Ok(Value::Function(Box::new(CurriedReducePlaceholder {
                    func: cf,
                    init_val: iv,
                    data: dv,
                })))
            }
        }

        Composition(f_vec, mode) => {
            if args.len() != 1 {
                return Err(format!("Composition => expected 1 argument, got {}", args.len()));
            }
            let mut current_val = args.into_iter().next().unwrap();
            match mode {
                ComposeMode::Compose => {
                    for fv_box in f_vec.iter().rev() {
                        current_val = apply_n_ary_function_value(interp, fv_box.clone(), vec![current_val])?;
                    }
                }
                ComposeMode::Pipe => {
                    for fv_box in f_vec.iter() {
                        current_val = apply_n_ary_function_value(interp, fv_box.clone(), vec![current_val])?;
                    }
                }
            }
            Ok(current_val)
        }
        CompositionPartial { mode, mut slots } => {
            for val in args {
                let mut assigned = false;
                for slot in slots.iter_mut() {
                    if slot.is_none() {
                        match val {
                            Value::Function(fb) => {
                                *slot = Some(fb);
                                assigned = true;
                                break;
                            }
                            Value::Placeholder => {
                                assigned = true;
                                break;
                            }
                            other => {
                                return Err(format!(
                                    "CompositionPartial => slot expects function, got {:?}",
                                    other
                                ));
                            }
                        }
                    }
                    if assigned {
                        break;
                    }
                }
                if !assigned {
                    return Err("CompositionPartial => no open slot or unexpected argument".to_string());
                }
            }
            if slots.iter().all(|mb| mb.is_some()) {
                let final_fvec: Vec<Box<FunctionValue>> =
                    slots.into_iter().map(|op| op.unwrap()).collect();
                Ok(Value::Function(Box::new(Composition(final_fvec, mode))))
            } else {
                Ok(Value::Function(Box::new(CompositionPartial { mode, slots })))
            }
        }

        CurriedProp(k) => {
            if args.is_empty() {
                return Ok(Value::Function(Box::new(CurriedProp(k))));
            }
            if args.len() > 1 {
                return Err(format!("CurriedProp => expected 0 or 1 arg, got {}", args.len()));
            }
            let obj_val = args.into_iter().next().unwrap();
            match obj_val {
                Value::Placeholder => Ok(Value::Function(Box::new(CurriedProp(k)))),
                Value::KeyedArray(map) => {
                    if let Some(v) = map.get(&k) {
                        Ok(v.clone())
                    } else {
                        Err(format!("CurriedProp => no key '{}' in keyed array", k))
                    }
                }
                other => Err(format!(
                    "CurriedProp => final object must be keyed array, got {:?}",
                    other
                )),
            }
        }

        CurriedAssoc(k, v) => {
            if args.is_empty() {
                return Ok(Value::Function(Box::new(CurriedAssoc(k, v))));
            }
            if args.len() > 1 {
                return Err(format!("CurriedAssoc => expected 1 argument, got {}", args.len()));
            }
            let obj_val = args.into_iter().next().unwrap();
            match obj_val {
                Value::KeyedArray(map) => {
                    let mut new_map = map.clone();
                    new_map.insert(k, v);
                    Ok(Value::KeyedArray(new_map))
                }
                other => Err(format!(
                    "CurriedAssoc => final object must be keyed array, got {:?}",
                    other
                )),
            }
        }

        RustClosure(desc, closure_arc, arity) => {
            if arity != 0 && args.len() != arity {
                return Err(format!(
                    "RustClosure({}): expected {} arg(s), got {}",
                    desc, arity, args.len()
                ));
            }
            let lock = closure_arc
                .lock()
                .map_err(|_| format!("Cannot lock RustClosure for '{}'", desc))?;
            lock(interp, args)
        }
    }
}

// ... unchanged do_map, do_filter, and call_inline_lambda_ast below ...

pub fn do_map(
    interp: &mut Interpreter,
    map_fn: &FunctionValue,
    data_val: Value,
) -> Result<Value, String> {
    use crate::parser::interpreter::integration::apply_one_function_value;
    match data_val {
        Value::IntArray(xs) => {
            let mut out = Vec::new();
            for x in xs {
                let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::Int(x))?;
                match ret {
                    Value::Int(i2) => out.push(i2),
                    other => {
                        return Err(format!("array:map => user function returned non-int: {:?}", other));
                    }
                }
            }
            Ok(Value::IntArray(out))
        }
        Value::StrArray(ss) => {
            let mut results = Vec::new();
            let mut out_kind: Option<&'static str> = None;
            for s in ss {
                let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::SingleString(s))?;
                match &ret {
                    Value::Int(_) => {
                        if out_kind.is_none() {
                            out_kind = Some("int");
                        } else if out_kind != Some("int") {
                            return Err("array:map => inconsistent return types from user function".to_string());
                        }
                    }
                    Value::SingleString(_) => {
                        if out_kind.is_none() {
                            out_kind = Some("string");
                        } else if out_kind != Some("string") {
                            return Err("array:map => inconsistent return types".to_string());
                        }
                    }
                    _ => return Err("array:map => unsupported return type from user function".to_string()),
                }
                results.push(ret);
            }
            match out_kind {
                None => Ok(Value::StrArray(vec![])),
                Some("int") => {
                    let mut out = Vec::with_capacity(results.len());
                    for item in results {
                        if let Value::Int(n) = item {
                            out.push(n);
                        }
                    }
                    Ok(Value::IntArray(out))
                }
                Some("string") => {
                    let mut out = Vec::with_capacity(results.len());
                    for item in results {
                        if let Value::SingleString(st) = item {
                            out.push(st);
                        }
                    }
                    Ok(Value::StrArray(out))
                }
                _ => Err("array:map => unrecognized type or mixing? (string vs int?)".to_string()),
            }
        }
        Value::FloatArray(ff) => {
            let mut out = Vec::new();
            for x in ff {
                let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::Float(x))?;
                match ret {
                    Value::Float(y) => out.push(y),
                    other => {
                        return Err(format!(
                            "array:map => user function returned non-float: {:?}",
                            other
                        ));
                    }
                }
            }
            Ok(Value::FloatArray(out))
        }
        Value::InkIterator(h) => {
            match &h.kind {
                InkIteratorKind::Core(state_arc) => {
                    let mut out = Vec::new();
                    let mut guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
                    while !guard.done && guard.current < guard.end {
                        let next_val = guard.current;
                        guard.current += 1;
                        if guard.current >= guard.end {
                            guard.done = true;
                        }
                        drop(guard);
                        let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), Value::Int(next_val))?;
                        match ret {
                            Value::Int(i2) => out.push(i2),
                            other => {
                                return Err(format!("array:map => user function returned non-int: {:?}", other));
                            }
                        }
                        guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
                    }
                    Ok(Value::IntArray(out))
                }
                InkIteratorKind::Plugin(plugin_arc) => {
                    let mut out = Vec::new();
                    let mut plugin = plugin_arc.lock().map_err(|_| "Plugin InkIterator lock error".to_string())?;
                    loop {
                        match plugin.next_value() {
                            Ok(val) => {
                                let ret = apply_one_function_value(interp, Box::new(map_fn.clone()), val)?;
                                match ret {
                                    Value::Int(i2) => out.push(i2),
                                    _ => return Err("array:map => user function returned non-int for plugin iterator".to_string()),
                                }
                            }
                            Err(e) if e == "NO_MORE_DATA" => break,
                            Err(e) => return Err(e),
                        }
                    }
                    Ok(Value::IntArray(out))
                }
            }
        }
        other => Err(format!(
            "array:map => only IntArray/StrArray/FloatArray + InkIterator supported, got {:?}",
            other
        )),
    }
}

pub fn do_filter(
    interp: &mut Interpreter,
    pred_fn: &FunctionValue,
    data_val: Value,
) -> Result<Value, String> {
    use crate::parser::interpreter::integration::apply_one_function_value;
    match data_val {
        Value::IntArray(xs) => {
            let mut out = Vec::new();
            for x in xs {
                let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), Value::Int(x))?;
                match ret {
                    Value::Bool(flag) if flag => out.push(x),
                    Value::Bool(false) => {},
                    other => return Err(format!("array:filter => user function returned non-bool: {:?}", other)),
                }
            }
            Ok(Value::IntArray(out))
        }
        Value::StrArray(ss) => {
            let mut out = Vec::new();
            for s in ss {
                let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), Value::SingleString(s.clone()))?;
                match ret {
                    Value::Bool(true) => out.push(s),
                    Value::Bool(false) => {},
                    other => return Err(format!("array:filter => user function returned non-bool: {:?}", other)),
                }
            }
            Ok(Value::StrArray(out))
        }
        Value::InkIterator(h) => {
            match &h.kind {
                InkIteratorKind::Core(state_arc) => {
                    let mut out = Vec::new();
                    let mut guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
                    while !guard.done && guard.current < guard.end {
                        let next_val = guard.current;
                        guard.current += 1;
                        if guard.current >= guard.end {
                            guard.done = true;
                        }
                        drop(guard);
                        let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), Value::Int(next_val))?;
                        match ret {
                            Value::Bool(true) => out.push(Value::Int(next_val)),
                            Value::Bool(false) => {},
                            _ => return Err("array:filter => user function returned non-bool for core iterator".to_string()),
                        }
                        guard = state_arc.lock().map_err(|_| "InkIteratorState lock error".to_string())?;
                    }
                    Ok(Value::IntArray(out.into_iter().map(|v| match v {
                        Value::Int(i) => i,
                        _ => unreachable!(),
                    }).collect()))
                }
                InkIteratorKind::Plugin(plugin_arc) => {
                    let mut out = Vec::new();
                    let mut plugin = plugin_arc.lock().map_err(|_| "Plugin InkIterator lock error".to_string())?;
                    loop {
                        match plugin.next_value() {
                            Ok(val) => {
                                let ret = apply_one_function_value(interp, Box::new(pred_fn.clone()), val.clone())?;
                                match ret {
                                    Value::Bool(true) => out.push(val),
                                    Value::Bool(false) => {},
                                    _ => return Err("array:filter => user function returned non-bool for plugin iterator".to_string()),
                                }
                            }
                            Err(e) if e == "NO_MORE_DATA" => break,
                            Err(e) => return Err(e),
                        }
                    }
                    Ok(Value::IntArray(out.into_iter().map(|v| match v {
                        Value::Int(i) => i,
                        _ => unreachable!(),
                    }).collect()))
                }
            }
        }
        other => Err(format!("array:filter => only IntArray/StrArray/InkIterator supported, got {:?}", other)),
    }
}

fn call_inline_lambda_ast(
    interp: &mut Interpreter,
    params: &[String],
    body_expr: &crate::parser::ast::Expr,
    arg_vals: Vec<Value>,
) -> Result<Value, String> {
    let mut old_vals = Vec::new();
    for (i, p) in params.iter().enumerate() {
        let old = interp.get_variables().get(p).cloned();
        old_vals.push((p.clone(), old));
        interp.set_variable(p, arg_vals[i].clone());
    }
    let out = interp.eval_expression(body_expr);
    for (p, maybe_val) in old_vals {
        if let Some(v) = maybe_val {
            interp.set_variable(&p, v);
        } else {
            interp.get_variables_mut().remove(&p);
        }
    }
    out
}
