//! Phase 43 Validation Pipeline.
//!
//! PRD: "Staffing outputs run through schema / completeness /
//! consistency / policy gates. Plug into Layer 5 execution loop —
//! failure triggers observer-correction iteration."
//!
//! This crate provides the `Validator` trait + `Artifact` enum +
//! Report/ValidationError types. Staffing validators (fill, email,
//! playbook) and the DevOps scaffold live in submodules.
//!
//! Landed 2026-04-24 as a scaffold — the trait + types + module
//! layout match the PRD; individual validator implementations are
//! `Unimplemented` stubs that return a clear "phase 43 not wired"
//! error rather than silently passing. The execution-loop integration
//! (generate → validate → correct → retry) comes in a follow-up
//! commit once the stubs are filled.

use serde::{Deserialize, Serialize};
use thiserror::Error;

pub mod staffing;
pub mod devops;

/// What a validator saw. One variant per artifact class we validate.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum Artifact {
    /// A fill proposal from the staffing executor — shape is
    /// `{fills: [{candidate_id, name}]}` per PRD.
    FillProposal(serde_json::Value),
    /// An email/SMS draft for outreach.
    EmailDraft(serde_json::Value),
    /// A playbook being sealed for memory.
    Playbook(serde_json::Value),
    /// Terraform plan output (scaffold, long-horizon).
    TerraformPlan(serde_json::Value),
    /// Ansible playbook (scaffold, long-horizon).
    AnsiblePlaybook(serde_json::Value),
}

/// Success report. Empty `findings` means a clean pass. Populated
/// findings with `Severity::Warning` means "acceptable but notable" —
/// the artifact passes. `Severity::Error` means validation failed;
/// the validator should return `Err(...)` in that case, not `Ok`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Report {
    pub findings: Vec<Finding>,
    pub elapsed_ms: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Finding {
    pub field: String,
    pub severity: Severity,
    pub message: String,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Severity {
    Warning,
    Error,
}

/// Validation failure — what went wrong + where + why. Returned as
/// `Err` from `validate`. Execution loop catches these and feeds them
/// to the observer-correction retry loop.
#[derive(Debug, Clone, Error, Serialize, Deserialize)]
pub enum ValidationError {
    /// Artifact schema doesn't match what we expected.
    #[error("schema mismatch at {field}: {reason}")]
    Schema { field: String, reason: String },
    /// Required data missing (e.g. endorsed count != target count).
    #[error("completeness: {reason}")]
    Completeness { reason: String },
    /// Data that's inconsistent with another source of truth
    /// (e.g. worker_id doesn't exist in the workers table).
    #[error("consistency: {reason}")]
    Consistency { reason: String },
    /// Policy violation — truth rule or access control said no.
    #[error("policy: {reason}")]
    Policy { reason: String },
    /// Validator hasn't been implemented yet — scaffold stub.
    #[error("validator not yet implemented for {artifact} — phase 43 scaffold")]
    Unimplemented { artifact: &'static str },
}

/// Core validation contract. Implementations live in `staffing::*` and
/// `devops::*`. The execution loop dispatches to the right impl based
/// on the Artifact variant.
pub trait Validator: Send + Sync {
    fn validate(&self, artifact: &Artifact) -> Result<Report, ValidationError>;
    /// Human-readable name for logs + Langfuse traces.
    fn name(&self) -> &'static str;
}
