//! Authority source parsing for wiki patterns
//!
//! Parses authority strings from wiki markdown into structured Authority enums,
//! enabling proper subject scheme generation (rfc://, owasp://, cwe://).

use regex::Regex;
use std::sync::OnceLock;

/// Structured authority source
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Authority {
    /// RFC with optional section
    RFC {
        /// RFC number
        num: u32,
        /// Optional section reference
        section: Option<String>,
    },
    /// OWASP with ID and optional year
    OWASP {
        /// OWASP identifier (e.g., "a03")
        id: String,
        /// Optional year (e.g., 2021)
        year: Option<u32>,
    },
    /// CWE (Common Weakness Enumeration)
    CWE {
        /// CWE identifier
        id: u32,
    },
    /// Unknown/unrecognized authority source
    Unknown(String),
}

/// Lazy-initialized regex patterns
static RFC_PATTERN: OnceLock<Regex> = OnceLock::new();
static OWASP_PATTERN: OnceLock<Regex> = OnceLock::new();
static CWE_PATTERN: OnceLock<Regex> = OnceLock::new();

fn rfc_pattern() -> &'static Regex {
    RFC_PATTERN.get_or_init(|| {
        // These regex patterns are simple and static - they will always compile
        Regex::new(r"(?i)rfc\s*(\d+)(?:\s+section\s+([0-9.]+))?")
            .unwrap_or_else(|_| unreachable!("RFC regex pattern is known to be valid"))
    })
}

fn owasp_pattern() -> &'static Regex {
    OWASP_PATTERN.get_or_init(|| {
        // These regex patterns are simple and static - they will always compile
        Regex::new(r"(?i)owasp\s+([a-z]\d+)(?::(\d{4}))?")
            .unwrap_or_else(|_| unreachable!("OWASP regex pattern is known to be valid"))
    })
}

fn cwe_pattern() -> &'static Regex {
    CWE_PATTERN.get_or_init(|| {
        // These regex patterns are simple and static - they will always compile
        Regex::new(r"(?i)cwe[-\s]*(\d+)")
            .unwrap_or_else(|_| unreachable!("CWE regex pattern is known to be valid"))
    })
}

/// Parse authority string into structured Authority enum
///
/// # Examples
///
/// ```
/// use aphoria::corpus::authority_parser::{parse_authority, Authority};
///
/// let auth = parse_authority("RFC 5246 Section 7.4.2");
/// assert_eq!(auth, Authority::RFC { num: 5246, section: Some("7.4.2".to_string()) });
///
/// let auth = parse_authority("OWASP A03:2021");
/// assert_eq!(auth, Authority::OWASP { id: "a03".to_string(), year: Some(2021) });
///
/// let auth = parse_authority("CWE-79");
/// assert_eq!(auth, Authority::CWE { id: 79 });
/// ```
pub fn parse_authority(authority_str: &str) -> Authority {
    let trimmed = authority_str.trim();

    // Try RFC pattern
    if let Some(caps) = rfc_pattern().captures(trimmed) {
        // Regex guarantees caps[1] is all digits, so parse will always succeed
        let num = caps[1].parse().unwrap_or_else(|_| unreachable!("regex matched \\d+"));
        let section = caps.get(2).map(|m| m.as_str().to_string());
        return Authority::RFC { num, section };
    }

    // Try OWASP pattern
    if let Some(caps) = owasp_pattern().captures(trimmed) {
        let id = caps[1].to_lowercase();
        let year = caps.get(2).and_then(|m| m.as_str().parse().ok());
        return Authority::OWASP { id, year };
    }

    // Try CWE pattern
    if let Some(caps) = cwe_pattern().captures(trimmed) {
        // Regex guarantees caps[1] is all digits, so parse will always succeed
        let id = caps[1].parse().unwrap_or_else(|_| unreachable!("regex matched \\d+"));
        return Authority::CWE { id };
    }

    // Fallback to unknown
    Authority::Unknown(trimmed.to_string())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_rfc_basic() {
        let auth = parse_authority("RFC 5246");
        assert_eq!(auth, Authority::RFC { num: 5246, section: None });
    }

    #[test]
    fn test_parse_rfc_with_section() {
        let auth = parse_authority("RFC 5246 Section 7.4.2");
        assert_eq!(auth, Authority::RFC { num: 5246, section: Some("7.4.2".to_string()) });
    }

    #[test]
    fn test_parse_rfc_lowercase() {
        let auth = parse_authority("rfc 7519");
        assert_eq!(auth, Authority::RFC { num: 7519, section: None });
    }

    #[test]
    fn test_parse_rfc_no_space() {
        let auth = parse_authority("RFC7519");
        assert_eq!(auth, Authority::RFC { num: 7519, section: None });
    }

    #[test]
    fn test_parse_owasp_with_year() {
        let auth = parse_authority("OWASP A03:2021");
        assert_eq!(auth, Authority::OWASP { id: "a03".to_string(), year: Some(2021) });
    }

    #[test]
    fn test_parse_owasp_without_year() {
        let auth = parse_authority("OWASP A01");
        assert_eq!(auth, Authority::OWASP { id: "a01".to_string(), year: None });
    }

    #[test]
    fn test_parse_owasp_lowercase() {
        let auth = parse_authority("owasp a03:2021");
        assert_eq!(auth, Authority::OWASP { id: "a03".to_string(), year: Some(2021) });
    }

    #[test]
    fn test_parse_cwe_hyphen() {
        let auth = parse_authority("CWE-79");
        assert_eq!(auth, Authority::CWE { id: 79 });
    }

    #[test]
    fn test_parse_cwe_space() {
        let auth = parse_authority("CWE 89");
        assert_eq!(auth, Authority::CWE { id: 89 });
    }

    #[test]
    fn test_parse_cwe_lowercase() {
        let auth = parse_authority("cwe-79");
        assert_eq!(auth, Authority::CWE { id: 79 });
    }

    #[test]
    fn test_parse_unknown() {
        let auth = parse_authority("Some Random Source");
        assert_eq!(auth, Authority::Unknown("Some Random Source".to_string()));
    }

    #[test]
    fn test_parse_owasp_cheat_sheet() {
        let auth = parse_authority("OWASP Password Storage Cheat Sheet");
        // Doesn't match pattern, falls back to Unknown
        matches!(auth, Authority::Unknown(_));
    }
}
