//! Compiled output types for log matchers.
//!
//! All regex fields are stored behind [`Arc`] so that [`CompiledMatcher`] is
//! cheaply cloneable and safe to share across threads.

use std::fmt;
use std::sync::Arc;

use regex::Regex;

// ---------------------------------------------------------------------------
// MatcherId
// ---------------------------------------------------------------------------

/// Unique identifier for a compiled matcher, e.g. `"rust.cargo.error"`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MatcherId(pub String);

impl fmt::Display for MatcherId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

// ---------------------------------------------------------------------------
// EmitSeverity
// ---------------------------------------------------------------------------

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EmitSeverity {
    Error,
    Warning,
    Info,
    Hint,
}

impl fmt::Display for EmitSeverity {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Error => write!(f, "error"),
            Self::Warning => write!(f, "warning"),
            Self::Info => write!(f, "info"),
            Self::Hint => write!(f, "hint"),
        }
    }
}

// ---------------------------------------------------------------------------
// EmitTemplate
// ---------------------------------------------------------------------------

/// Template describing the diagnostic to emit when a matcher fires.
///
/// Template strings may contain `{{ capture_name }}` placeholders that are
/// substituted with named regex capture group values at runtime.
#[derive(Debug, Clone)]
pub struct EmitTemplate {
    pub severity: EmitSeverity,
    /// Required template for the primary message.
    pub message: String,
    pub file: Option<String>,
    pub line: Option<String>,
    pub column: Option<String>,
    pub code: Option<String>,
}

// ---------------------------------------------------------------------------
// EndCondition
// ---------------------------------------------------------------------------

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EndCondition {
    /// The block ends when the next `start` pattern matches another line.
    NextStart,
    /// The block ends on a blank line.
    BlankLine,
}

// ---------------------------------------------------------------------------
// BodyRule
// ---------------------------------------------------------------------------

#[derive(Debug, Clone)]
pub struct BodyRule {
    pub pattern: Arc<Regex>,
    /// If `true`, this rule may be skipped if the line does not match.
    pub optional: bool,
    /// If `true`, this rule may match zero or more consecutive lines.
    pub repeat: bool,
}

// ---------------------------------------------------------------------------
// CompiledMatcher
// ---------------------------------------------------------------------------

/// A fully-compiled, thread-safe log matcher ready for use by the parsing engine.
///
/// Regex fields are behind [`Arc`] so cloning is O(1) and the type is
/// `Send + Sync`.
#[derive(Debug, Clone)]
pub struct CompiledMatcher {
    pub id: MatcherId,
    pub source: String,
    pub priority: u32,
    pub schema_version: u32,
    pub start: Arc<Regex>,
    pub body: Vec<BodyRule>,
    /// Optional safety cap on accumulated body lines before force-emitting.
    pub max_lines: Option<u32>,
    pub end: EndCondition,
    pub emit: EmitTemplate,
}

// Compile-time assertion: CompiledMatcher must be Send + Sync.
// Arc<Regex> satisfies this because Regex is Send + Sync.
fn _assert_send_sync() {
    fn require<T: Send + Sync>() {}
    require::<CompiledMatcher>();
}
