// src/parser/interpreter/array_helpers.rs

use crate::parser::types::{FunctionValue, Value, InkIteratorKind};
use crate::parser::interpreter::Interpreter;

pub fn do_map(
    interp: &mut Interpreter,
    map_fn: &FunctionValue,
    data_val: Value,
) -> Result<Value, String> {
    use crate::parser::interpreter::apply::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::apply::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)),
    }
}

// Curried placeholders for map/filter/reduce

pub fn apply_curried_map_placeholder(
    interp: &mut Interpreter,
    mut func: Option<Box<FunctionValue>>,
    mut data: Option<Value>,
    args: Vec<Value>,
) -> Result<Value, String> {
    for val in args {
        if func.is_none() {
            match val {
                Value::Function(fb) => func = Some(fb),
                Value::Placeholder => {},
                other => {
                    return Err(format!(
                        "CurriedMapPlaceholder => first param must be function or '_', got {:?}",
                        other
                    ));
                }
            }
        } else if data.is_none() {
            match val {
                Value::Placeholder => {},
                x => data = Some(x),
            }
        } else {
            return Err("array:map => extra argument(s) after function & data".to_string());
        }
    }
    if let (Some(mf), Some(dat)) = (func.as_ref(), data.as_ref()) {
        do_map(interp, mf, dat.clone())
    } else {
        Ok(Value::Function(Box::new(FunctionValue::CurriedMapPlaceholder {
            func,
            data,
        })))
    }
}

pub fn apply_curried_filter_placeholder(
    interp: &mut Interpreter,
    mut pred: Option<Box<FunctionValue>>,
    mut data: Option<Value>,
    args: Vec<Value>,
) -> Result<Value, String> {
    for val in args {
        if pred.is_none() {
            match val {
                Value::Function(fb) => pred = Some(fb),
                Value::Placeholder => {},
                other => {
                    return Err(format!(
                        "CurriedFilterPlaceholder => first param must be function or '_', got {:?}",
                        other
                    ));
                }
            }
        } else if data.is_none() {
            match val {
                Value::Placeholder => {},
                v => data = Some(v),
            }
        } else {
            return Err("array:filter => extra arg(s) after function & data".to_string());
        }
    }
    if let (Some(pf), Some(dat)) = (pred.as_ref(), data.as_ref()) {
        do_filter(interp, pf, dat.clone())
    } else {
        Ok(Value::Function(Box::new(FunctionValue::CurriedFilterPlaceholder {
            pred,
            data,
        })))
    }
}

pub fn apply_curried_reduce_partial(
    _interp: &mut Interpreter,
    f_box: Box<FunctionValue>,
    init_val: Value,
    args: Vec<Value>,
) -> Result<Value, String> {
    if args.is_empty() {
        Ok(Value::Function(Box::new(FunctionValue::CurriedReducePlaceholder {
            func: Some(f_box),
            init_val: Some(init_val),
            data: None,
        })))
    } else if args.len() == 1 {
        Err("reduce => not fully implemented".to_string())
    } else {
        Err(format!("CurriedReducePartial => expected 0 or 1 arg, got {}", args.len()))
    }
}

pub fn apply_curried_reduce_placeholder(
    _interp: &mut Interpreter,
    mut func: Option<Box<FunctionValue>>,
    mut init_val: Option<Value>,
    mut data: Option<Value>,
    args: Vec<Value>,
) -> Result<Value, String> {
    for val in args {
        if func.is_none() {
            match val {
                Value::Function(fb) => func = Some(fb),
                Value::Placeholder => {},
                other => {
                    return Err(format!(
                        "array:reduce => first param must be function or '_', got {:?}",
                        other
                    ));
                }
            }
        } else if init_val.is_none() {
            match val {
                Value::Placeholder => {},
                x => init_val = Some(x),
            }
        } else if data.is_none() {
            match val {
                Value::Placeholder => {},
                x => data = Some(x),
            }
        } else {
            return Err("array:reduce => extra arg(s) after function, init, data".to_string());
        }
    }
    if let (Some(_f), Some(_ini), Some(_dat)) = (func.as_ref(), init_val.as_ref(), data.as_ref()) {
        Err("reduce => final bridging not fully implemented".to_string())
    } else {
        Ok(Value::Function(Box::new(FunctionValue::CurriedReducePlaceholder {
            func,
            init_val,
            data,
        })))
    }
}
