//! GitLab Code Quality schema validation tests.
//!
//! GitLab's Code Quality format is a JSON array of finding objects with
//! specific required fields. We codify the schema once here (no upstream
//! JSON Schema exists), and validate every flavor of report we render.
//! Required fields per the GitLab documentation
//! (https://docs.gitlab.com/ee/ci/testing/code_quality.html):
//!   - `description` (string)
//!   - `fingerprint` (string)
//!   - `severity` (info / minor / major / critical / blocker)
//!   - `location.path` (string)
//!   - `location.lines.begin` (integer)

use jsonschema::Validator;
use mcptest_core::report::reporters::GitlabReporter;
use mcptest_core::report::{Report, Reporter};
use serde_json::{json, Value};

fn gitlab_schema() -> Validator {
    let schema = json!({
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "GitLab Code Quality finding array",
        "type": "array",
        "items": {
            "type": "object",
            "required": ["description", "fingerprint", "severity", "location"],
            "properties": {
                "description": { "type": "string" },
                "check_name": { "type": "string" },
                "fingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
                "severity": {
                    "type": "string",
                    "enum": ["info", "minor", "major", "critical", "blocker"]
                },
                "location": {
                    "type": "object",
                    "required": ["path", "lines"],
                    "properties": {
                        "path": { "type": "string" },
                        "lines": {
                            "type": "object",
                            "required": ["begin"],
                            "properties": {
                                "begin": { "type": "integer", "minimum": 1 }
                            }
                        }
                    }
                },
                "properties": { "type": "object" }
            }
        }
    });
    jsonschema::validator_for(&schema).expect("GitLab schema compiles")
}

fn render(report: &Report) -> Value {
    let mut buf = Vec::new();
    GitlabReporter::default()
        .render(report, &mut buf)
        .expect("GitLab render");
    serde_json::from_slice(&buf).expect("rendered GitLab parses")
}

#[test]
fn rendered_gitlab_for_sample_report_validates_against_schema() {
    let validator = gitlab_schema();
    let doc = render(&Report::sample());
    let errors: Vec<_> = validator.iter_errors(&doc).collect();
    if !errors.is_empty() {
        for e in &errors {
            eprintln!("GitLab validation error: {e}");
        }
        panic!("rendered GitLab output did not validate");
    }
}

#[test]
fn rendered_gitlab_for_sample_full_report_validates_against_schema() {
    let validator = gitlab_schema();
    let doc = render(&Report::sample_full());
    let errors: Vec<_> = validator.iter_errors(&doc).collect();
    if !errors.is_empty() {
        for e in &errors {
            eprintln!("GitLab validation error: {e}");
        }
        panic!("rendered GitLab output (sample_full) did not validate");
    }
}

#[test]
fn rendered_gitlab_for_all_pass_report_validates_against_schema() {
    let validator = gitlab_schema();
    let doc = render(&Report::sample_all_pass());
    let errors: Vec<_> = validator.iter_errors(&doc).collect();
    if !errors.is_empty() {
        for e in &errors {
            eprintln!("GitLab validation error: {e}");
        }
        panic!("rendered GitLab output (sample_all_pass) did not validate");
    }
}

#[test]
fn malformed_gitlab_finding_fails_schema_validation() {
    // Sanity check: a finding without a severity must be rejected,
    // otherwise the validator could let a regression slip through
    // unnoticed.
    let validator = gitlab_schema();
    let bad = json!([{
        "description": "missing severity",
        "fingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "location": { "path": "x", "lines": { "begin": 1 } }
    }]);
    let errors: Vec<_> = validator.iter_errors(&bad).collect();
    assert!(
        !errors.is_empty(),
        "validator should reject a finding missing severity"
    );
}
