//! JSON wrappers for car-verify functions.

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

/// Verify a proposal from JSON inputs. Returns result JSON.
///
/// When `tool_schemas_json` is supplied (a JSON array of `ToolSchema`
/// objects, e.g. `[{"name":"echo","parameters":{...}}]`), each
/// `tool_call`'s parameters are validated against the matching
/// schema — catching type mismatches and missing required fields, not
/// just unknown tools (car-releases#56). `tool_names` remains
/// supported for existence-only checks when no schemas are at hand;
/// `tool_schemas_json` takes precedence when both are passed.
pub fn verify(
    proposal_json: &str,
    initial_state_json: Option<&str>,
    tool_names: Option<Vec<String>>,
    max_actions: usize,
    tool_schemas_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 result = if let Some(schemas_json) = tool_schemas_json {
        let schemas: Vec<ToolSchema> = serde_json::from_str(schemas_json)
            .map_err(|e| format!("invalid tool schemas JSON: {}", e))?;
        let map: HashMap<String, ToolSchema> =
            schemas.into_iter().map(|s| (s.name.clone(), s)).collect();
        car_verify::verify_with_schemas(&proposal, initial_state.as_ref(), Some(&map), max_actions)
    } else {
        let tools: Option<HashSet<String>> = tool_names.map(|v| v.into_iter().collect());
        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,
        // Evidence bundle: the verifier's declared scope — checks run,
        // assumptions, untested regions, residual risks, coverage
        // confidence (survey "Code as Agent Harness" §5.2.2).
        "evidence": result.evidence,
    }))
    .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 a proposal for transactional conflicts against the current shared
/// state (survey §4.3/§5.2.4). `versions_json` is a JSON object mapping
/// state key → current version (from `StateStore::versions`);
/// `state_json` (optional) maps key → current value for value-level
/// assumption checks. Returns the `TransactionReport` JSON:
/// `{ consistent, conflicts: [{ kind, key, actions, explanation,
/// resolution }] }`.
pub fn transaction_check(
    proposal_json: &str,
    versions_json: Option<&str>,
    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 versions: HashMap<String, u64> = versions_json
        .map(serde_json::from_str)
        .transpose()
        .map_err(|e| format!("invalid versions JSON: {e}"))?
        .unwrap_or_default();
    let state: Option<HashMap<String, Value>> = state_json
        .map(serde_json::from_str)
        .transpose()
        .map_err(|e| format!("invalid state JSON: {e}"))?;
    let report = car_verify::check_transaction(&proposal, &versions, state.as_ref());
    serde_json::to_string(&report).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))
}
