// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2026 ndaal Gesellschaft für Sicherheit in der Informationstechnik mbH & Co KG, Cologne
// SPDX-FileCopyrightText: Author: Pierre Gronau <Pierre.Gronau@ndaal.eu>

//! Validate-a-file page: schema-validate a SARIF document on disk *without*
//! importing it into the store. Always runs the dual 2.1.0 + 2.2 check.

use http::{Response, StatusCode};
use serde_json::Value;

use crate::app_state::AppState;
use crate::html::{self, alert, escape, Nav};
use crate::router::{html_response, Body};
use crate::validation;

use super::{first, query_pairs};

/// The validate form card.
fn form() -> String {
    "<h1 class=\"h3 mb-4\">Validate SARIF</h1>\
     <div class=\"card shadow-sm mb-4\"><div class=\"card-body\">\
     <p class=\"text-body-secondary\">Schema-validate a <span class=\"mono\">.sarif</span>/<span class=\"mono\">.json</span> file on disk \
     against the SARIF 2.1.0 schema and the SARIF 2.2 draft schema, without loading it. \
     Use the dashboard's per-log <em>Validate</em> action to check an already-loaded log.</p>\
     <form class=\"row g-2\" method=\"get\" action=\"/validate/run\">\
     <div class=\"col\"><input type=\"text\" name=\"path\" class=\"form-control\" \
     placeholder=\"/absolute/path/to/report.sarif\" required></div>\
     <div class=\"col-auto\"><button class=\"btn btn-primary\" type=\"submit\">\
     <i class=\"bi bi-check2-circle me-1\"></i>Validate</button></div>\
     </form></div></div>"
        .to_owned()
}

/// `GET /validate` — the validate form.
pub fn page(
    _state: AppState,
    _parts: http::request::Parts,
    _params: Vec<(String, String)>,
) -> Response<Body> {
    html_response(StatusCode::OK, html::page("Validate", Nav::Validate, &form()))
}

/// Render the form followed by an error alert.
fn with_error(message: &str) -> Response<Body> {
    let body = format!("{}{}", form(), alert("danger", &escape(message)));
    html_response(StatusCode::OK, html::page("Validate", Nav::Validate, &body))
}

/// `GET /validate/run?path=…` — validate the file and show the report.
pub fn run(
    _state: AppState,
    parts: http::request::Parts,
    _params: Vec<(String, String)>,
) -> Response<Body> {
    let pairs = query_pairs(&parts.uri);
    let path = first(&pairs, "path").unwrap_or("").trim().to_owned();
    if path.is_empty() {
        return with_error("Please provide a path.");
    }
    // Operator-chosen path to validate (no confinement boundary).
    // See skills/rust-path-security.md.
    let bytes = match std::fs::read(&path) { // allow-std-fs: operator-chosen path
        Ok(bytes) => bytes,
        Err(error) => return with_error(&format!("cannot read {path}: {error}")),
    };
    let doc: Value = match serde_json::from_slice(&bytes) {
        Ok(doc) => doc,
        Err(error) => return with_error(&format!("{path} is not valid JSON: {error}")),
    };
    let report = match validation::validate(&doc, true) {
        Ok(report) => report,
        Err(error) => return with_error(&error),
    };

    let title = format!("Validation — {path}");
    let content = format!(
        "<h1 class=\"h3 mb-4\">{}</h1>{}\
         <a class=\"btn btn-outline-secondary\" href=\"/validate\"><i class=\"bi bi-arrow-left me-1\"></i>Validate another file</a>",
        escape(&title),
        super::home::report_html("Schema validation report", &report),
    );
    html_response(StatusCode::OK, html::page("Validation", Nav::Validate, &content))
}
