//! OSCAL assessment MCP tool.
//!
//! Emits a standard OSCAL assessment-results document for a repo's findings —
//! what breakpilot's scanner MCP client pulls. Mapped findings target their
//! compliance controls (via the stamped `control_refs`); unmapped findings are
//! reported as-is, so nothing is lost.

use mongodb::bson::doc;
use rmcp::{model::*, ErrorData as McpError};
use schemars::JsonSchema;
use serde::Deserialize;

use compliance_core::models::oscal_assessment::assess;
use compliance_core::models::Finding;

use crate::database::Database;

#[derive(Debug, Deserialize, JsonSchema)]
pub struct OscalAssessmentParams {
    /// Repository / target id to assess.
    pub repo_id: String,
}

pub async fn oscal_assessment(
    db: &Database,
    params: OscalAssessmentParams,
) -> Result<CallToolResult, McpError> {
    let mut cursor = db
        .findings()
        .find(doc! { "repo_id": &params.repo_id })
        .await
        .map_err(|e| McpError::internal_error(format!("DB error: {e}"), None))?;

    let mut findings: Vec<Finding> = Vec::new();
    while cursor
        .advance()
        .await
        .map_err(|e| McpError::internal_error(format!("cursor error: {e}"), None))?
    {
        findings.push(
            cursor
                .deserialize_current()
                .map_err(|e| McpError::internal_error(format!("deserialize error: {e}"), None))?,
        );
    }

    let document = assess(&findings, chrono::Utc::now());
    let json = serde_json::to_string_pretty(&document)
        .map_err(|e| McpError::internal_error(format!("json error: {e}"), None))?;
    Ok(CallToolResult::success(vec![Content::text(json)]))
}
