//! (Experimental) Output capturing.
//!
//! This module provides a way to emit metadata to the JS caller.

use std::cell::RefCell;

use better_scoped_tls::scoped_tls;
use rustc_hash::FxHashMap;

scoped_tls!(static OUTPUT: RefCell<FxHashMap<String, String>>);

/// (Experimental) Captures output.
///
/// This is not stable and may be removed in the future.
pub fn capture<Ret>(f: impl FnOnce() -> Ret) -> (Ret, FxHashMap<String, String>) {
    let output = RefCell::new(Default::default());

    let ret = OUTPUT.set(&output, f);

    (ret, output.into_inner())
}

#[cfg(all(feature = "plugin-mode", target_arch = "wasm32"))]
extern "C" {
    fn __emit_output(output_ptr: u32, output_len: u32);
}

/// (Experimental) Emits a value to the JS caller.
///
/// This is not stable and may be removed in the future.
#[cfg(all(feature = "plugin-mode", target_arch = "wasm32"))]
pub fn experimental_emit(key: String, value: String) {
    let output = (key, value);

    let diag = swc_common::plugin::serialized::PluginSerializedBytes::try_serialize(
        &swc_common::plugin::serialized::VersionedSerializable::new(output),
    )
    .expect("Should able to serialize String");
    let (ptr, len) = diag.as_ptr();

    unsafe {
        __emit_output(ptr as u32, len as u32);
    }
}

/// (Experimental) Emits a value to the JS caller.
///
/// This is not stable and may be removed in the future.
#[cfg(not(all(feature = "plugin-mode", target_arch = "wasm32")))]
pub fn experimental_emit(key: String, value: String) {
    OUTPUT.with(|output| {
        let previous = output.borrow_mut().insert(key, value);

        if let Some(previous) = previous {
            panic!("Key already set. Previous value: {previous:?}");
        }
    });
}
