use crate::parser::ast::{Expr, DestructurePattern};
use crate::parser::types::Value;
use crate::parser::interpreter::Interpreter;
use std::sync::{Arc, Mutex};

pub fn bool_like(v: &Value) -> Result<bool, String> {
    match v {
        Value::Bool(b) => Ok(*b),
        Value::Int(i) => Ok(*i != 0),
        Value::Float(ff) => Ok(*ff != 0.0),
        Value::Long(l) => Ok(*l != 0),
        _ => Err(format!("Cannot interpret as boolean: {:?}", v)),
    }
}

pub fn eval_bracketed_array(interp: &mut Interpreter, exprs: &[Expr]) -> Result<Value, String> {
    let mut subvals = Vec::with_capacity(exprs.len());
    for e in exprs {
        let val = interp.eval_expression(e)?;
        subvals.push(val);
    }
    if subvals.is_empty() {
        return Ok(Value::IntArray(vec![]));
    }

    if subvals.iter().all(|v| matches!(v, Value::SingleString(_))) {
        let ss = subvals.into_iter().map(|v| match v {
            Value::SingleString(s) => s,
            _ => unreachable!(),
        }).collect();
        return Ok(Value::StrArray(ss));
    }

    let is_all_arrays = subvals.iter().all(|v| matches!(v, Value::IntArray(_) | Value::FloatArray(_) | Value::StrArray(_) | Value::BoolArray(_)));
    let same_len = if is_all_arrays {
        let first_len = match &subvals[0] {
            Value::IntArray(xs) => xs.len(),
            Value::FloatArray(xs) => xs.len(),
            Value::StrArray(xs) => xs.len(),
            Value::BoolArray(xs) => xs.len(),
            _ => 0,
        };
        subvals.iter().all(|v| match v {
            Value::IntArray(xs) => xs.len() == first_len,
            Value::FloatArray(xs) => xs.len() == first_len,
            Value::StrArray(xs) => xs.len() == first_len,
            Value::BoolArray(xs) => xs.len() == first_len,
            _ => false,
        })
    } else {
        false
    };

    if is_all_arrays && same_len && !subvals.is_empty() {
        let mut only_int = true;
        let mut has_float = false;
        for v in &subvals {
            match v {
                Value::FloatArray(_) => {
                    only_int = false;
                    has_float = true;
                }
                Value::IntArray(_) => {}
                _ => {
                    only_int = false;
                }
            }
        }
        if only_int {
            let mut int_rows = Vec::new();
            for v in subvals {
                match v {
                    Value::IntArray(xs) => int_rows.push(xs),
                    _ => {}
                }
            }
            return Ok(Value::Int2DArray(int_rows));
        } else if has_float && subvals.iter().all(|v| matches!(v, Value::IntArray(_) | Value::FloatArray(_))) {
            let mut float_rows = Vec::new();
            for v in subvals {
                match v {
                    Value::IntArray(xs) => float_rows.push(xs.iter().map(|&i| i as f64).collect()),
                    Value::FloatArray(fs) => float_rows.push(fs),
                    _ => {}
                }
            }
            return Ok(Value::Float2DArray(float_rows));
        } else if subvals.iter().all(|v| matches!(v, Value::FloatArray(_))) {
            let mut float_rows = Vec::new();
            for v in subvals {
                match v {
                    Value::FloatArray(fs) => float_rows.push(fs),
                    _ => {}
                }
            }
            return Ok(Value::Float2DArray(float_rows));
        } else {
            return Ok(Value::MixedArray(subvals));
        }
    }

    let mut found_int = false;
    let mut found_float = false;
    let mut found_bool = false;
    let mut found_str = false;
    let mut found_other = false;
    let mut ints = Vec::new();
    let mut floats = Vec::new();
    let mut bools = Vec::new();
    let mut strs = Vec::new();

    for item in &subvals {
        match item {
            Value::Int(i) => {
                if found_str || found_bool || found_other {
                    found_other = true;
                } else if found_float {
                    floats.push(*i as f64);
                } else {
                    found_int = true;
                    ints.push(*i);
                }
            }
            Value::Float(ff) => {
                if found_str || found_bool || found_other {
                    found_other = true;
                } else {
                    if !found_float {
                        found_float = true;
                        for old_i in ints.drain(..) {
                            floats.push(old_i as f64);
                        }
                    }
                    floats.push(*ff);
                }
            }
            Value::Long(l) => {
                if found_str || found_bool || found_other {
                    found_other = true;
                } else if found_float {
                    floats.push(*l as f64);
                } else {
                    found_int = true;
                    ints.push(*l as i32);
                }
            }
            Value::Bool(b) => {
                if found_int || found_float || found_str || found_other {
                    found_other = true;
                } else {
                    found_bool = true;
                    bools.push(*b);
                }
            }
            Value::SingleString(s) => {
                if found_int || found_float || found_bool || found_other {
                    found_other = true;
                } else {
                    found_str = true;
                    strs.push(s.clone());
                }
            }
            _ => {
                found_other = true;
            }
        }
    }

    if found_other {
        return Ok(Value::MixedArray(subvals));
    }
    if found_float {
        return Ok(Value::FloatArray(floats));
    }
    if found_int {
        return Ok(Value::IntArray(ints));
    }
    if found_bool {
        return Ok(Value::BoolArray(bools));
    }
    if found_str {
        return Ok(Value::StrArray(strs));
    }
    Ok(Value::IntArray(vec![]))
}

pub fn eval_keyed_array(interp: &mut Interpreter, kv_pairs: &[(String, Expr)]) -> Result<Value, String> {
    use indexmap::IndexMap;
    let mut map = IndexMap::new();
    for (k, subexpr) in kv_pairs {
        let val = interp.eval_expression(subexpr)?;
        map.insert(k.clone(), val);
    }
    Ok(Value::KeyedArray(map))
}

pub fn assign_to_expr(interp: &mut Interpreter, left_expr: &Expr, value: Value) -> Result<(), String> {
    match left_expr {
        Expr::Ident { name, .. } => {
            interp.set_variable(name, value);
            Ok(())
        }
        Expr::Index { base, index, .. } => {
            assign_index(interp, base, index, value)
        }
        _ => Err("Left side of assignment must be a variable or array index".to_string()),
    }
}

pub fn assign_keyed_destructure(interp: &mut Interpreter, pattern: &DestructurePattern, value: Value) -> Result<(), String> {
    match pattern {
        DestructurePattern::Keyed(keys) => {
            match value {
                Value::KeyedArray(map) => {
                    for k in keys {
                        if let Some(v) = map.get(k) {
                            interp.set_variable(k, v.clone());
                        } else {
                            interp.set_variable(k, Value::Placeholder);
                        }
                    }
                    Ok(())
                }
                other => Err(format!("Keyed destructure requires a KeyedArray, got {:?}", other)),
            }
        }
        DestructurePattern::Tuple(names) => {
            match value {
                Value::IntArray(xs) => {
                    for (i, name) in names.iter().enumerate() {
                        if i < xs.len() {
                            interp.set_variable(name, Value::Int(xs[i]));
                        } else {
                            interp.set_variable(name, Value::Placeholder);
                        }
                    }
                    Ok(())
                }
                Value::FloatArray(xs) => {
                    for (i, name) in names.iter().enumerate() {
                        if i < xs.len() {
                            interp.set_variable(name, Value::Float(xs[i]));
                        } else {
                            interp.set_variable(name, Value::Placeholder);
                        }
                    }
                    Ok(())
                }
                Value::StrArray(xs) => {
                    for (i, name) in names.iter().enumerate() {
                        if i < xs.len() {
                            interp.set_variable(name, Value::SingleString(xs[i].clone()));
                        } else {
                            interp.set_variable(name, Value::Placeholder);
                        }
                    }
                    Ok(())
                }
                Value::MixedArray(xs) => {
                    for (i, name) in names.iter().enumerate() {
                        if i < xs.len() {
                            interp.set_variable(name, xs[i].clone());
                        } else {
                            interp.set_variable(name, Value::Placeholder);
                        }
                    }
                    Ok(())
                }
                v => {
                    for name in names {
                        interp.set_variable(name, v.clone());
                    }
                    Ok(())
                }
            }
        }
    }
}

fn extract_mutex_for_indexing(val: &Value) -> Option<Arc<Mutex<Value>>> {
    match val {
        Value::Ref(arc_mutex) => Some(arc_mutex.clone()),
        _ => None,
    }
}

fn assign_index(interp: &mut Interpreter, base: &Expr, idx_expr: &Expr, new_val: Value) -> Result<(), String> {
    let base_val = interp.eval_expression(base)?;
    let idx_val = interp.eval_expression(idx_expr)?;

    // If base_val is a reference, mutate through it
    if let Some(arc_mutex) = extract_mutex_for_indexing(&base_val) {
        let mut mut_val = arc_mutex.lock().unwrap();
        return match (&mut *mut_val, idx_val, new_val) {
            (Value::IntArray(ref mut xs), Value::Int(i), Value::Int(new_i)) => {
                if i < 0 || (i as usize) >= xs.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                xs[i as usize] = new_i;
                Ok(())
            }
            (Value::FloatArray(ref mut xs), Value::Int(i), Value::Float(new_f)) => {
                if i < 0 || (i as usize) >= xs.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                xs[i as usize] = new_f;
                Ok(())
            }
            (Value::FloatArray(ref mut xs), Value::Int(i), Value::Int(new_i)) => {
                if i < 0 || (i as usize) >= xs.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                xs[i as usize] = new_i as f64;
                Ok(())
            }
            (Value::StrArray(ref mut xs), Value::Int(i), Value::SingleString(new_s)) => {
                if i < 0 || (i as usize) >= xs.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                xs[i as usize] = new_s;
                Ok(())
            }
            (Value::Int2DArray(ref mut rows), Value::Int(i), Value::IntArray(new_row)) => {
                if i < 0 || (i as usize) >= rows.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                rows[i as usize] = new_row;
                Ok(())
            }
            (Value::Float2DArray(ref mut rows), Value::Int(i), Value::FloatArray(new_row)) => {
                if i < 0 || (i as usize) >= rows.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                rows[i as usize] = new_row;
                Ok(())
            }
            (Value::Float2DArray(ref mut rows), Value::Int(i), Value::IntArray(new_row)) => {
                if i < 0 || (i as usize) >= rows.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                rows[i as usize] = new_row.iter().map(|&n| n as f64).collect();
                Ok(())
            }
            (Value::MixedArray(ref mut items), Value::Int(i), nv) => {
                if i < 0 || (i as usize) >= items.len() {
                    return Err(format!("Array index out of range: {}", i));
                }
                items[i as usize] = nv;
                Ok(())
            }
            _ => Err("Unsupported or type-mismatched index assignment through Ref".to_string()),
        };
    }

    // Otherwise: treat as non-reference (legacy value copy logic)
    match (base_val, idx_val, new_val) {
        (Value::IntArray(xs), Value::Int(i), Value::Int(new_i)) => {
            if i < 0 || (i as usize) >= xs.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = xs.clone();
            copy[i as usize] = new_i;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::IntArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::IntArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::FloatArray(xs), Value::Int(i), Value::Float(new_f)) => {
            if i < 0 || (i as usize) >= xs.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = xs.clone();
            copy[i as usize] = new_f;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::FloatArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::FloatArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::FloatArray(xs), Value::Int(i), Value::Int(new_i)) => {
            if i < 0 || (i as usize) >= xs.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = xs.clone();
            copy[i as usize] = new_i as f64;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::FloatArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::FloatArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::StrArray(xs), Value::Int(i), Value::SingleString(new_s)) => {
            if i < 0 || (i as usize) >= xs.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = xs.clone();
            copy[i as usize] = new_s;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::StrArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::StrArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::Int2DArray(rows), Value::Int(i), Value::IntArray(new_row)) => {
            if i < 0 || (i as usize) >= rows.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = rows.clone();
            copy[i as usize] = new_row;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::Int2DArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::Int2DArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::Float2DArray(rows), Value::Int(i), Value::FloatArray(new_row)) => {
            if i < 0 || (i as usize) >= rows.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = rows.clone();
            copy[i as usize] = new_row;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::Float2DArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::Float2DArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::Float2DArray(rows), Value::Int(i), Value::IntArray(new_row)) => {
            if i < 0 || (i as usize) >= rows.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            let mut copy = rows.clone();
            copy[i as usize] = new_row.iter().map(|&n| n as f64).collect();
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::Float2DArray(copy));
                    Ok(())
                }
                Expr::Index { base: prev_base, index: prev_idx, .. } => {
                    assign_index(interp, prev_base, prev_idx, Value::Float2DArray(copy))
                }
                _ => Err("Unsupported nested indexing base".to_string()),
            }
        }
        (Value::MixedArray(mut items), Value::Int(i), nv) => {
            if i < 0 || (i as usize) >= items.len() {
                return Err(format!("Array index out of range: {}", i));
            }
            items[i as usize] = nv;
            match base {
                Expr::Ident { name, .. } => {
                    interp.set_variable(name, Value::MixedArray(items));
                    Ok(())
                }
                Expr::Index { base: prev_b, index: prev_i, .. } => {
                    assign_index(interp, prev_b, prev_i, Value::MixedArray(items))
                }
                _ => Err("Unsupported nested indexing base for MixedArray".to_string()),
            }
        }
        _ => Err(format!("Cannot assign via array index with provided types.")),
    }
}

pub fn deep_flatten_upcast_array(items: &[Value]) -> Value {
    let mut floats = Vec::new();
    let mut ints = Vec::new();
    let mut strs = Vec::new();
    let mut found_float = false;
    let mut found_int = false;
    let mut found_str = false;
    let mut found_other = false;

    fn flatten(
        v: &Value,
        ints: &mut Vec<i32>,
        floats: &mut Vec<f64>,
        strs: &mut Vec<String>,
        found_int: &mut bool,
        found_float: &mut bool,
        found_str: &mut bool,
        found_other: &mut bool,
    ) {
        match v {
            Value::Int(i) => {
                *found_int = true;
                ints.push(*i);
            }
            Value::Float(f) => {
                *found_float = true;
                floats.push(*f);
            }
            Value::SingleString(s) => {
                *found_str = true;
                strs.push(s.clone());
            }
            Value::StrArray(ss) => {
                *found_str = true;
                strs.extend(ss.clone());
            }
            Value::IntArray(xs) => {
                *found_int = true;
                ints.extend(xs.iter().cloned());
            }
            Value::FloatArray(fs) => {
                *found_float = true;
                floats.extend(fs.iter().cloned());
            }
            Value::MixedArray(inner) => for x in inner {
                flatten(x, ints, floats, strs, found_int, found_float, found_str, found_other)
            },
            _ => {
                *found_other = true;
            }
        }
    }
    for v in items {
        flatten(v, &mut ints, &mut floats, &mut strs, &mut found_int, &mut found_float, &mut found_str, &mut found_other);
    }

    if found_other {
        let mut out = Vec::new();
        for v in items {
            match v {
                Value::MixedArray(inner) => out.extend(inner.clone()),
                _ => out.push(v.clone())
            }
        }
        Value::MixedArray(out)
    } else if found_float || (found_int && found_float) {
        let mut all_floats = floats;
        all_floats.extend(ints.iter().map(|&i| i as f64));
        Value::FloatArray(all_floats)
    } else if found_int {
        Value::IntArray(ints)
    } else if found_str {
        Value::StrArray(strs)
    } else {
        Value::MixedArray(items.to_vec())
    }
}
