//! RFC normative statement corpus builder.
//!
//! This builder fetches RFCs from the IETF RFC Editor and extracts normative
//! statements (MUST, SHALL, SHOULD per RFC 2119) to create authoritative
//! assertions.
//!
//! # Caching
//!
//! RFC text is cached to `~/.cache/aphoria/rfc-cache/rfc{number}.txt` to
//! minimize network requests.
//!
//! # Target RFCs
//!
//! | RFC  | Topic                  | Priority Claims                                    |
//! |------|------------------------|----------------------------------------------------|
//! | 7519 | JWT                    | audience_validation, expiry_validation, signature  |
//! | 6749 | OAuth 2.0              | redirect_uri_validation, state_parameter           |
//! | 6750 | Bearer tokens          | transport_security                                 |
//! | 8446 | TLS 1.3                | cert_verification, cipher_selection               |
//! | 7525 | TLS best practices     | hostname_verification                              |
//! | 6238 | TOTP                   | time_step, validation_window                       |
//! | 7617 | HTTP Basic Auth        | transport_security                                 |
//! | 9110 | HTTP Semantics         | timeout_handling                                   |

mod parsers;
#[cfg(test)]
mod tests;

use std::fs;
use std::time::Duration;

use ed25519_dalek::SigningKey;
use stemedb_core::types::{Assertion, SourceClass};
use tracing::{debug, info, instrument, warn};

use super::CorpusBuilder;
use crate::config::CorpusConfig;
use crate::episteme::create_authoritative_assertion;
use crate::AphoriaError;
use parsers::parse_normative_statements;

/// Default RFCs to fetch when none are specified.
const DEFAULT_RFCS: &[u32] = &[
    7519, // JWT
    6749, // OAuth 2.0
    6750, // Bearer tokens
    8446, // TLS 1.3
    7525, // TLS best practices
    6238, // TOTP
    7617, // HTTP Basic Auth
    9110, // HTTP Semantics
];

/// HTTP timeout for fetching RFCs.
const FETCH_TIMEOUT_SECS: u64 = 30;

/// Builder for RFC normative statement corpus.
pub struct RfcCorpusBuilder {
    /// List of RFC numbers to fetch.
    rfc_list: Vec<u32>,
}

impl RfcCorpusBuilder {
    /// Create a new RFC corpus builder with specified RFCs.
    pub fn new(rfc_list: &Option<Vec<u32>>) -> Self {
        let list = rfc_list.clone().unwrap_or_else(|| DEFAULT_RFCS.to_vec());
        Self { rfc_list: list }
    }

    /// Create a builder with default RFC list.
    pub fn with_defaults() -> Self {
        Self { rfc_list: DEFAULT_RFCS.to_vec() }
    }
}

impl Default for RfcCorpusBuilder {
    fn default() -> Self {
        Self::with_defaults()
    }
}

impl CorpusBuilder for RfcCorpusBuilder {
    fn name(&self) -> &str {
        "RFC"
    }

    fn scheme(&self) -> &str {
        "rfc"
    }

    fn default_tier(&self) -> u8 {
        0 // Regulatory
    }

    fn requires_network(&self) -> bool {
        true // Needs to fetch RFCs (unless cached)
    }

    fn source_ids(&self) -> Vec<String> {
        self.rfc_list.iter().map(|n| format!("RFC {}", n)).collect()
    }

    #[instrument(skip(self, signing_key, config), fields(builder = "RFC", rfcs = ?self.rfc_list))]
    fn build(
        &self,
        signing_key: &SigningKey,
        timestamp: u64,
        config: &CorpusConfig,
    ) -> Result<Vec<Assertion>, AphoriaError> {
        let cache_dir = config.cache_dir.join("rfc-cache");
        fs::create_dir_all(&cache_dir)?;

        let mut all_assertions = Vec::new();

        for &rfc_num in &self.rfc_list {
            match fetch_and_parse_rfc(rfc_num, &cache_dir, signing_key, timestamp) {
                Ok(assertions) => {
                    info!(rfc = rfc_num, assertions = assertions.len(), "Parsed RFC");
                    all_assertions.extend(assertions);
                }
                Err(e) => {
                    warn!(rfc = rfc_num, error = %e, "Failed to process RFC");
                    // Continue with other RFCs
                }
            }
        }

        Ok(all_assertions)
    }
}

/// Fetch an RFC and parse its normative statements.
fn fetch_and_parse_rfc(
    rfc_num: u32,
    cache_dir: &std::path::Path,
    signing_key: &SigningKey,
    timestamp: u64,
) -> Result<Vec<Assertion>, AphoriaError> {
    let text = fetch_rfc_text(rfc_num, cache_dir)?;
    let statements = parse_normative_statements(&text, rfc_num);

    let assertions = statements
        .into_iter()
        .map(|stmt| {
            create_authoritative_assertion(
                signing_key,
                &stmt.subject,
                &stmt.predicate,
                stmt.value,
                SourceClass::Regulatory, // Tier 0
                &stmt.description,
                timestamp,
            )
        })
        .collect();

    Ok(assertions)
}

/// Fetch RFC text, using cache if available.
fn fetch_rfc_text(rfc_num: u32, cache_dir: &std::path::Path) -> Result<String, AphoriaError> {
    let cache_path = cache_dir.join(format!("rfc{}.txt", rfc_num));

    // Check cache first
    if cache_path.exists() {
        debug!(rfc = rfc_num, "Loading from cache");
        return fs::read_to_string(&cache_path)
            .map_err(|e| AphoriaError::RfcFetch { rfc: rfc_num, message: e.to_string() });
    }

    // Fetch from network
    let url = format!("https://www.rfc-editor.org/rfc/rfc{}.txt", rfc_num);
    info!(rfc = rfc_num, url = %url, "Fetching RFC");

    let response = ureq::get(&url)
        .timeout(Duration::from_secs(FETCH_TIMEOUT_SECS))
        .call()
        .map_err(|e| AphoriaError::RfcFetch { rfc: rfc_num, message: e.to_string() })?;

    let text = response
        .into_string()
        .map_err(|e| AphoriaError::RfcFetch { rfc: rfc_num, message: e.to_string() })?;

    // Cache the result
    if let Err(e) = fs::write(&cache_path, &text) {
        warn!(rfc = rfc_num, error = %e, "Failed to cache RFC");
    }

    Ok(text)
}
