//! `workflow::` Rhai bridge — start a durable workflow run (v1.2 M5).
//!
//! ```rhai
//! let run_id = workflow::start("nightly_report", #{ date: "2026-07-12" });
//! let run_id = workflow::start("cleanup"); // no input
//! ```
//!
//! Returns the run id as a string. The owning workflow resolves from
//! `cx.app_id` inside the service — never a script arg (the isolation
//! boundary). The durable orchestrator advances the run asynchronously; the
//! script does not block on completion.

use std::sync::Arc;

use picloud_shared::{SdkCallCx, Services, WorkflowService};
use rhai::{Dynamic, Engine as RhaiEngine, EvalAltResult, Module};
use tokio::runtime::Handle as TokioHandle;

use crate::sdk::bridge::{dynamic_to_json_capped, MAX_JSON_MATERIALIZE_BYTES};

pub(super) fn register(engine: &mut RhaiEngine, services: &Services, cx: Arc<SdkCallCx>) {
    let svc = services.workflow.clone();
    let mut module = Module::new();

    // `workflow::start(name, input)`
    {
        let svc = svc.clone();
        let cx = cx.clone();
        module.set_native_fn(
            "start",
            move |name: &str, input: Dynamic| -> Result<String, Box<EvalAltResult>> {
                start_blocking(&svc, &cx, name, &input)
            },
        );
    }

    // `workflow::start(name)` — no input (passes JSON null).
    {
        let svc = svc.clone();
        let cx = cx.clone();
        module.set_native_fn(
            "start",
            move |name: &str| -> Result<String, Box<EvalAltResult>> {
                start_blocking(&svc, &cx, name, &Dynamic::UNIT)
            },
        );
    }

    engine.register_static_module("workflow", module.into());
}

fn start_blocking(
    svc: &Arc<dyn WorkflowService>,
    cx: &Arc<SdkCallCx>,
    name: &str,
    input: &Dynamic,
) -> Result<String, Box<EvalAltResult>> {
    let input_json = if input.is_unit() {
        serde_json::Value::Null
    } else {
        dynamic_to_json_capped(input, MAX_JSON_MATERIALIZE_BYTES)?
    };
    let handle = TokioHandle::try_current().map_err(|e| -> Box<EvalAltResult> {
        EvalAltResult::ErrorRuntime(
            format!("workflow::start: no tokio runtime available: {e}").into(),
            rhai::Position::NONE,
        )
        .into()
    })?;
    let svc = svc.clone();
    let cx = cx.clone();
    let name = name.to_string();
    let run_id = handle
        .block_on(async move { svc.start(&cx, &name, input_json).await })
        .map_err(|e| -> Box<EvalAltResult> {
            EvalAltResult::ErrorRuntime(
                format!("workflow::start: {e}").into(),
                rhai::Position::NONE,
            )
            .into()
        })?;
    Ok(run_id.to_string())
}
