//! JSON wrappers for car-verify functions.

use car_ir::ActionProposal;
use serde_json::Value;
use std::collections::{HashMap, HashSet};

/// Verify a proposal from JSON inputs. Returns result JSON.
pub fn verify(
    proposal_json: &str,
    initial_state_json: Option<&str>,
    tool_names: Option<Vec<String>>,
    max_actions: usize,
) -> Result<String, String> {
    let proposal: ActionProposal =
        serde_json::from_str(proposal_json).map_err(|e| format!("invalid proposal JSON: {}", e))?;
    let initial_state: Option<HashMap<String, Value>> = initial_state_json
        .map(|s| serde_json::from_str(s))
        .transpose()
        .map_err(|e| format!("invalid state JSON: {}", e))?;
    let tools: Option<HashSet<String>> = tool_names.map(|v| v.into_iter().collect());

    let result = car_verify::verify(
        &proposal,
        initial_state.as_ref(),
        tools.as_ref(),
        max_actions,
    );

    serde_json::to_string(&serde_json::json!({
        "valid": result.valid,
        "issues": result.issues.iter().map(|i| serde_json::json!({
            "action_id": i.action_id,
            "severity": i.severity,
            "message": i.message,
        })).collect::<Vec<_>>(),
        "simulated_state": result.simulated_state,
        "execution_levels": result.execution_levels,
        "conflicts": result.conflicts,
    }))
    .map_err(|e| e.to_string())
}

/// Simulate a proposal from JSON inputs. Returns state JSON.
pub fn simulate(proposal_json: &str, initial_state_json: Option<&str>) -> Result<String, String> {
    let proposal: ActionProposal =
        serde_json::from_str(proposal_json).map_err(|e| format!("invalid proposal JSON: {}", e))?;
    let initial_state: Option<HashMap<String, Value>> = initial_state_json
        .map(|s| serde_json::from_str(s))
        .transpose()
        .map_err(|e| format!("invalid state JSON: {}", e))?;

    let state = car_verify::simulate(&proposal, initial_state.as_ref());
    serde_json::to_string(&state).map_err(|e| e.to_string())
}

/// Optimize a proposal from JSON. Returns optimized proposal JSON.
pub fn optimize(proposal_json: &str) -> Result<String, String> {
    let proposal: ActionProposal =
        serde_json::from_str(proposal_json).map_err(|e| format!("invalid proposal JSON: {}", e))?;
    let optimized = car_verify::optimize(&proposal);
    serde_json::to_string(&optimized).map_err(|e| e.to_string())
}

/// Check equivalence of two proposals from JSON.
pub fn equivalent(p1_json: &str, p2_json: &str) -> Result<bool, String> {
    let p1: ActionProposal =
        serde_json::from_str(p1_json).map_err(|e| format!("invalid proposal1 JSON: {}", e))?;
    let p2: ActionProposal =
        serde_json::from_str(p2_json).map_err(|e| format!("invalid proposal2 JSON: {}", e))?;
    Ok(car_verify::equivalent(&p1, &p2, None))
}
