//! Compiled regex patterns and key-name predicates for cassette normalization.
//!
//! Pulled into a small submodule so the main `normalize.rs` stays focused on
//! the walk and match logic, and so we can swap or extend a pattern without
//! reading the whole tree-walk.

use std::sync::OnceLock;

use regex::Regex;

/// True when the parent JSON key looks like a trace, request, or correlation
/// identifier and should be opaquely placeholdered regardless of value shape.
pub(super) fn is_trace_key(key: &str) -> bool {
    let lower = key.to_ascii_lowercase();
    matches!(
        lower.as_str(),
        "traceid" | "trace_id" | "requestid" | "request_id" | "correlationid" | "correlation_id"
    )
}

/// True when the parent JSON key is exactly `etag` (case-insensitive). HTTP
/// ETag values look like ordinary quoted strings, so we only treat them as
/// non-deterministic when the field name confirms it.
pub(super) fn is_etag_key(key: &str) -> bool {
    key.eq_ignore_ascii_case("etag")
}

/// Anchored ISO-8601 timestamp regex.
pub(super) fn timestamp_regex() -> &'static Regex {
    static RE: OnceLock<Regex> = OnceLock::new();
    RE.get_or_init(|| {
        Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$")
            .expect("timestamp regex is a const literal")
    })
}

/// Anchored UUID v4 regex (variant byte must be `8`, `9`, `a`, or `b`).
pub(super) fn uuid_regex() -> &'static Regex {
    static RE: OnceLock<Regex> = OnceLock::new();
    RE.get_or_init(|| {
        Regex::new(r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$")
            .expect("uuid regex is a const literal")
    })
}

/// Plausible Unix epoch regex: 10 digits (seconds) or 13 digits (millis),
/// both starting `17`, `18`, or `19` so we only catch post-2020 timestamps.
pub(super) fn epoch_regex() -> &'static Regex {
    static RE: OnceLock<Regex> = OnceLock::new();
    RE.get_or_init(|| {
        Regex::new(r"^(?:1[7-9]\d{8}|1[7-9]\d{11})$").expect("epoch regex is a const literal")
    })
}

/// Quoted or weak-quoted ETag value regex.
pub(super) fn etag_regex() -> &'static Regex {
    static RE: OnceLock<Regex> = OnceLock::new();
    RE.get_or_init(|| Regex::new(r#"^(W/)?"[^"]+"$"#).expect("etag regex is a const literal"))
}
