use crate::models::AttributeValue;
use crate::prediction_additions::{
build_transition_graph, calculate_rework_score, extract_prefix_features,
};
use crate::state::{get_or_init_state, StoredObject};
use serde_json::json;
use std::collections::{BTreeMap, HashMap};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn extract_prefix_features_wasm(prefix_json: &str) -> Result<JsValue, JsValue> {
let prefix: Vec<String> = serde_json::from_str(prefix_json)
.map_err(|e| crate::error::js_val(&format!("Invalid prefix JSON: {}", e)))?;
let features = extract_prefix_features(&prefix);
let result = json!({
"length": features.length,
"last_activity": features.last_activity,
"unique_activities": features.unique_activities,
"rework_count": features.rework_count,
"activity_frequency_entropy": features.activity_frequency_entropy,
});
let serialized = serde_json::to_string(&result)
.map_err(|e| crate::error::js_val(&format!("Serialization error: {}", e)))?;
Ok(crate::error::js_val(&serialized))
}
#[wasm_bindgen]
pub fn compute_rework_score(trace_json: &str) -> Result<JsValue, JsValue> {
let trace: Vec<String> = serde_json::from_str(trace_json)
.map_err(|e| crate::error::js_val(&format!("Invalid trace JSON: {}", e)))?;
let rework_count = calculate_rework_score(&trace);
let denominator = if trace.len() > 1 { trace.len() - 1 } else { 1 };
let rework_ratio = rework_count as f64 / denominator as f64;
let repeated_pairs: Vec<String> = trace
.windows(2)
.filter(|w| w[0] == w[1])
.map(|w| format!("{}→{}", w[0], w[1]))
.collect();
let result = json!({
"rework_count": rework_count,
"rework_ratio": rework_ratio,
"repeated_pairs": repeated_pairs,
});
let serialized = serde_json::to_string(&result)
.map_err(|e| crate::error::js_val(&format!("Serialization error: {}", e)))?;
Ok(crate::error::js_val(&serialized))
}
#[wasm_bindgen]
pub fn build_transition_probabilities(
log_handle: &str,
activity_key: &str,
) -> Result<JsValue, JsValue> {
let activity_key = activity_key.to_string();
let result_json = get_or_init_state().with_event_log(log_handle, |log| {
let mut edge_counts: BTreeMap<(String, String), usize> = BTreeMap::new();
let mut activity_totals: HashMap<String, usize> = HashMap::new();
for trace in &log.traces {
let mut prev_act: Option<String> = None;
for event in &trace.events {
if let Some(AttributeValue::String(act)) = event.attributes.get(&activity_key) {
*activity_totals.entry(act.clone()).or_default() += 1;
if let Some(ref prev) = prev_act {
*edge_counts.entry((prev.clone(), act.clone())).or_default() += 1;
}
prev_act = Some(act.clone());
}
}
}
let tg = build_transition_graph(log, &activity_key);
let edges: Vec<serde_json::Value> = edge_counts
.iter()
.map(|((from, to), &count)| {
let total = activity_totals.get(from).copied().unwrap_or(1);
let probability = count as f64 / total as f64;
json!({
"from": from,
"to": to,
"probability": probability,
"count": count,
})
})
.collect();
let result = json!({
"edges": edges,
"activities": tg.activities,
});
serde_json::to_string(&result)
.map_err(|e| crate::error::js_val(&format!("Serialization error: {}", e)))
})?;
Ok(crate::error::js_val(&result_json))
}