// src/modules/compose.rs

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

/// `compose(...)` => right-to-left function composition
pub fn compose_bridge_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    handle_compose_or_pipe(true, args)
}

/// `pipe(...)` => left-to-right function composition
pub fn pipe_bridge_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    handle_compose_or_pipe(false, args)
}

fn handle_compose_or_pipe(is_compose: bool, raw_args: Vec<Value>) -> Result<Value, String> {
    let mode = if is_compose {
        ComposeMode::Compose
    } else {
        ComposeMode::Pipe
    };

    // Because CompositionPartial now stores `Vec<Option<Box<FunctionValue>>>`,
    // we must store Box<FunctionValue> when we see Value::Function(fb).
    let mut slots: Vec<Option<Box<FunctionValue>>> = Vec::new();

    for val in raw_args {
        match val {
            Value::Placeholder => {
                // push None => means waiting for a function
                slots.push(None);
            }
            Value::Function(fb) => {
                // store the Box<FunctionValue> directly
                slots.push(Some(fb));
            }
            other => {
                let name = if is_compose { "compose" } else { "pipe" };
                return Err(format!(
                    "{}(...) => each param must be a Function(...) or `_`, got {:?}",
                    name, other
                ));
            }
        }
    }

    // If all slots are filled => produce a Composition, else a CompositionPartial
    let all_filled = slots.iter().all(|mb| mb.is_some());
    if all_filled {
        // unwrap each slot => Box<FunctionValue>, store in a Vec<Box<FunctionValue>>
        let fvec = slots
            .into_iter()
            .map(|mb| mb.unwrap())
            .collect::<Vec<Box<FunctionValue>>>();

        let final_comp = Composition(fvec, mode);
        Ok(Value::Function(Box::new(final_comp)))
    } else {
        let partial = CompositionPartial { mode, slots };
        Ok(Value::Function(Box::new(partial)))
    }
}

pub fn register_compose_and_pipe(interp: &mut Interpreter) {
    let cfn: DynamicFn = Arc::new(Mutex::new(compose_bridge_fn));
    interp.register_dynamic_function("compose", cfn.clone());
    interp.set_variable(
        "compose",
        Value::Function(Box::new(FunctionValue::Named("compose".to_string())))
    );

    let pfn: DynamicFn = Arc::new(Mutex::new(pipe_bridge_fn));
    interp.register_dynamic_function("pipe", pfn.clone());
    interp.set_variable(
        "pipe",
        Value::Function(Box::new(FunctionValue::Named("pipe".to_string())))
    );
}
