//! Retrospectives (APS-V1-0003.RT01) substandard.
//!
//! This module is scaffolded. The full validator implementation lands in a
//! follow up PR. The normative contract lives in `docs/01_spec.md`; this
//! module exposes the diagnostic code constants and shared defaults so the
//! parent crate and downstream tooling can reference them today.

use apss_core::{Diagnostic, Diagnostics};
use std::path::Path;

/// Diagnostic codes emitted by the Retrospectives substandard.
///
/// See `docs/01_spec.md` Section 9 for descriptions.
pub mod error_codes {
    pub const DIR_NOT_FOUND: &str = "RETRO01-dir-not-found";
    pub const NAMING_MISMATCH: &str = "RETRO01-naming-mismatch";
    pub const INVALID_NAMING_REGEX: &str = "RETRO01-invalid-naming-regex";
    pub const FRONTMATTER_MISSING: &str = "RETRO01-frontmatter-missing";
    pub const FRONTMATTER_FIELD_MISSING: &str = "RETRO01-frontmatter-field-missing";
    pub const INVALID_STATUS: &str = "RETRO01-invalid-status";
    pub const MISSING_SECTION: &str = "RETRO01-missing-section";
    pub const HISTORY_MODIFIED: &str = "RETRO01-history-modified";
}

/// Allowed lifecycle status values for retrospectives.
pub const ALLOWED_STATUSES: &[&str] = &["proposed", "active", "deprecated", "superseded"];

/// Required H2 section titles in a retrospective.
pub const REQUIRED_SECTIONS: &[&str] = &["Context", "What Went Well", "What Did Not", "Followups"];

/// Default directory relative to the repo root.
pub const DEFAULT_DIRECTORY: &str = "docs/retrospectives";

/// Default naming pattern (3 to 5 digit numeric block, kebab slug, `.md`).
///
/// The file prefix matches the substandard id (RETRO01 substandard, RETRO
/// file prefix) the same way ADR01 substandard uses an ADR file prefix.
pub const DEFAULT_NAMING_PATTERN: &str = r"RETRO-\d{3,5}-[a-zA-Z0-9-]+\.md";

/// Sentinel marking the start of an append-only region inside a retrospective.
pub const APPEND_REGION_SENTINEL: &str = "<!-- RETRO01:append-only -->";

/// Scaffolded validator entry point.
///
/// Returns an empty diagnostic set today. The implementation lands in a
/// follow up PR per the spec.
pub fn validate(_repo_root: &Path) -> Diagnostics {
    Diagnostics::new()
}

/// Placeholder factory for the canonical `RETRO01-dir-not-found` diagnostic so
/// downstream code can already assert against the contract.
#[doc(hidden)]
pub fn dir_not_found_diagnostic(dir: &Path) -> Diagnostic {
    Diagnostic::error(
        error_codes::DIR_NOT_FOUND,
        format!("Retrospective directory not found: {}", dir.display()),
    )
    .with_path(dir)
    .with_hint(format!(
        "Create the directory at '{}' or set docs.retrospectives.disable = true in apss.yaml",
        dir.display()
    ))
}

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

    #[test]
    fn allowed_statuses_match_parent_vocabulary() {
        assert_eq!(
            ALLOWED_STATUSES,
            &["proposed", "active", "deprecated", "superseded"]
        );
    }

    #[test]
    fn required_sections_present_in_order() {
        assert_eq!(
            REQUIRED_SECTIONS,
            &["Context", "What Went Well", "What Did Not", "Followups"]
        );
    }

    #[test]
    fn default_naming_pattern_compiles_and_accepts_expected_range() {
        let re = regex::Regex::new(&format!("^{DEFAULT_NAMING_PATTERN}$")).unwrap();
        assert!(re.is_match("RETRO-001-q1-launch.md"));
        assert!(re.is_match("RETRO-99999-late-stage.md"));
        assert!(!re.is_match("RETRO-01-too-short.md"));
        assert!(!re.is_match("RETRO-123456-too-long.md"));
        assert!(!re.is_match("RETRO-001-q1-launch")); // no .md
    }

    #[test]
    fn validate_returns_empty_today() {
        let temp = tempfile::tempdir().unwrap();
        let report = validate(temp.path());
        assert_eq!(report.len(), 0);
    }
}
