use std::sync::{Arc, Mutex};
use std::io::{Write, stdout};

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

/// Print the value **without** a trailing newline, returning the original data unchanged.
/// If it's an InkIterator or InkTransform, we consume it non-blockingly (with polling).
fn sput_direct(interp: &mut Interpreter, data: Value) -> Result<Value, String> {
    if interp.is_verbose() {
        eprintln!("[sput] => About to print data with no newline. data={:?}", data);
    }

    match data {
        // 1) If it is an InkIterator => register a poller to print values as they become available
        Value::InkIterator(handle) => {
            if interp.is_verbose() {
                eprintln!("[sput] => encountered InkIterator => register poller for non-blocking iteration");
            }
            let poller_handle = handle.clone();
            let return_handle = handle;
            let poller: PollerFn = Arc::new(Mutex::new(move |interp: &mut Interpreter| {
                let mut items_printed = 0;
                match &poller_handle.kind {
                    InkIteratorKind::Core(state_arc) => {
                        let mut guard = state_arc.lock().unwrap();
                        while !guard.done && guard.current < guard.end {
                            let item_val = guard.current;
                            guard.current += 1;
                            if guard.current >= guard.end {
                                guard.done = true;
                            }
                            drop(guard);

                            print!("{}", item_val);
                            let _ = stdout().flush();
                            interp.push_print(&format!("{}", item_val));

                            items_printed += 1;
                            break;
                        }
                    }
                    InkIteratorKind::Plugin(plugin_arc) => {
                        let mut plugin = plugin_arc.lock().unwrap();
                        match plugin.next_value() {
                            Ok(val) => {
                                print!("{}", item_val_to_string(&val));
                                let _ = stdout().flush();
                                interp.push_print(&item_val_to_string(&val));
                                items_printed += 1;
                            }
                            Err(e) if e == "NO_MORE_DATA" => {}
                            Err(e) => {
                                eprintln!("[sput] => transform error => {}", e);
                            }
                        }
                    }
                }
                if items_printed > 0 {
                    items_printed
                } else {
                    0
                }
            }));
            interp.add_poller(poller);

            Ok(Value::InkIterator(return_handle))
        }

        // 2) If it is an InkTransform => register poller for non-blocking transform
        Value::InkTransform(fb) => {
            if interp.is_verbose() {
                eprintln!("[sput] => encountered InkTransform => register poller for non-blocking iteration");
            }
            let poller_fb = fb.clone();
            let return_fb = fb;
            let poller: PollerFn = Arc::new(Mutex::new(move |interp: &mut Interpreter| {
                let mut items_printed = 0;
                match &*poller_fb {
                    FunctionValue::RustClosure(_desc, closure_arc, _arity) => {
                        let lock_cl = closure_arc.lock().unwrap();
                        match lock_cl(&mut *interp, vec![]) {
                            Ok(item_val) => {
                                let txt = format!("{}", item_val_to_string(&item_val));
                                print!("{}", txt);
                                let _ = stdout().flush();
                                interp.push_print(&txt);
                                items_printed += 1;
                            }
                            Err(e) => {
                                if e == "NO_MORE_DATA" {
                                    // done
                                } else {
                                    eprintln!("[sput] => transform error => {}", e);
                                }
                            }
                        }
                    }
                    _ => {
                        // Possibly a composition or some other variant => no iteration
                    }
                }
                if items_printed > 0 {
                    items_printed
                } else {
                    0
                }
            }));
            interp.add_poller(poller);

            Ok(Value::InkTransform(return_fb))
        }

        // 3) Otherwise => print once, no newline
        other => {
            let text = item_val_to_string(&other);
            if interp.is_verbose() {
                eprintln!("[sput] => printing => '{}'", text);
            }
            print!("{}", text);
            let _ = stdout().flush();
            interp.push_print(&text);
            Ok(other)
        }
    }
}

/// Helper to convert a single Value to string for printing.
fn item_val_to_string(v: &Value) -> String {
    match v {
        Value::SingleString(s) => s.clone(),
        Value::Int(i) => i.to_string(),
        Value::Float(ff) => ff.to_string(),
        Value::Bool(b) => b.to_string(),
        Value::Long(l) => l.to_string(),
        _ => format!("{:?}", v),
    }
}

/// The bridging function for `sput(...)`.
/// Expects exactly one argument, else error.
/// Calls `sput_direct` to handle printing or iteration.
fn sput_bridge_fn(interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("sput(...) => expected exactly 1 argument, got {}", args.len()));
    }
    let data = args.remove(0);
    sput_direct(interp, data)
}

/// Register the `sput` dynamic function & variable name: `sput`.
pub fn register_sput(interp: &mut Interpreter) {
    if interp.is_verbose() {
        eprintln!("[sput] => register_sput => setting up bridging for sput(...)");
    }
    let f: DynamicFn = Arc::new(Mutex::new(sput_bridge_fn));
    let info = DynamicFnInfo::new(f, false);
    interp.register_dynamic_function_ex("sput", info);

    // Also create a top-level variable => "sput"
    interp.set_variable(
        "sput",
        Value::Function(Box::new(FunctionValue::Named("sput".to_string())))
    );
}
