/// Result of validating a commit message.
#[derive(Debug, PartialEq)]
pub enum ValidationResult {
    /// The commit message follows the Conventional Commits format.
    Valid,
    /// The commit message should be skipped (merge, revert, initial commit).
    Skip,
    /// The commit message is empty.
    EmptyMessage,
    /// The commit message does not follow the Conventional Commits format.
    Invalid,
}

/// The commit types accepted by the Conventional Commits specification.
pub const VALID_TYPES: &[&str] = &[
    "feat", "fix", "docs", "style", "refactor", "test", "chore", "build", "ci", "perf", "revert",
];

/// Validates a commit message against the Conventional Commits specification.
///
/// Format: `<type>(<optional scope>)(!)?:  <description>`
///
/// Merge commits, revert commits, and initial commits are automatically skipped.
pub fn validate_commit_message(message: &str) -> ValidationResult {
    // Extract first line and trim whitespace
    let first_line = message.lines().next().unwrap_or("").trim();

    if first_line.is_empty() {
        return ValidationResult::EmptyMessage;
    }

    // Auto-skip merge commits, reverts, and initial commits
    if first_line.starts_with("Merge branch")
        || first_line.starts_with("Revert")
        || first_line.starts_with("Initial commit")
    {
        return ValidationResult::Skip;
    }

    // Validate against Conventional Commits regex
    use regex::Regex;
    use std::sync::LazyLock;

    static RE: LazyLock<Regex> = LazyLock::new(|| {
        Regex::new(
            r"^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\([a-zA-Z0-9_.\-]+\))?(!)?:\s+.+$",
        )
        .expect("invalid regex")
    });

    if RE.is_match(first_line) {
        ValidationResult::Valid
    } else {
        ValidationResult::Invalid
    }
}

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

    // -------------------------------------------------------
    // Empty / whitespace-only messages
    // -------------------------------------------------------

    #[test]
    fn empty_message_is_rejected() {
        assert_eq!(validate_commit_message(""), ValidationResult::EmptyMessage);
    }

    #[test]
    fn whitespace_only_message_is_rejected() {
        assert_eq!(
            validate_commit_message("   "),
            ValidationResult::EmptyMessage
        );
    }

    #[test]
    fn newline_only_message_is_rejected() {
        assert_eq!(
            validate_commit_message("\n\n"),
            ValidationResult::EmptyMessage
        );
    }

    // -------------------------------------------------------
    // Auto-skip messages (merge, revert, initial commit)
    // -------------------------------------------------------

    #[test]
    fn merge_branch_is_skipped() {
        assert_eq!(
            validate_commit_message("Merge branch 'feature/x' into main"),
            ValidationResult::Skip
        );
    }

    #[test]
    fn merge_branch_simple_is_skipped() {
        assert_eq!(
            validate_commit_message("Merge branch 'develop'"),
            ValidationResult::Skip
        );
    }

    #[test]
    fn revert_commit_is_skipped() {
        assert_eq!(
            validate_commit_message("Revert \"feat: add authentication\""),
            ValidationResult::Skip
        );
    }

    #[test]
    fn revert_plain_is_skipped() {
        assert_eq!(
            validate_commit_message("Revert some change"),
            ValidationResult::Skip
        );
    }

    #[test]
    fn initial_commit_is_skipped() {
        assert_eq!(
            validate_commit_message("Initial commit"),
            ValidationResult::Skip
        );
    }

    // -------------------------------------------------------
    // Valid conventional commit messages — each type
    // -------------------------------------------------------

    #[test]
    fn valid_feat() {
        assert_eq!(
            validate_commit_message("feat: add user authentication"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_fix() {
        assert_eq!(
            validate_commit_message("fix: resolve null pointer in parser"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_docs() {
        assert_eq!(
            validate_commit_message("docs: update README"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_style() {
        assert_eq!(
            validate_commit_message("style: fix indentation"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_refactor() {
        assert_eq!(
            validate_commit_message("refactor: extract method"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_test() {
        assert_eq!(
            validate_commit_message("test: add unit tests for parser"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_chore() {
        assert_eq!(
            validate_commit_message("chore: update dependencies"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_build() {
        assert_eq!(
            validate_commit_message("build: configure webpack"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_ci() {
        assert_eq!(
            validate_commit_message("ci: add GitHub Actions workflow"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_perf() {
        assert_eq!(
            validate_commit_message("perf: optimize database query"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_revert_type() {
        assert_eq!(
            validate_commit_message("revert: undo feature flag"),
            ValidationResult::Valid
        );
    }

    // -------------------------------------------------------
    // Valid — with scope
    // -------------------------------------------------------

    #[test]
    fn valid_with_scope() {
        assert_eq!(
            validate_commit_message("feat(auth): add JWT support"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_scope_with_dot() {
        assert_eq!(
            validate_commit_message("fix(api.v2): handle timeout"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_scope_with_hyphen() {
        assert_eq!(
            validate_commit_message("docs(user-guide): add examples"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_scope_with_underscore() {
        assert_eq!(
            validate_commit_message("refactor(data_layer): simplify queries"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_scope_with_numbers() {
        assert_eq!(
            validate_commit_message("fix(issue123): patch security flaw"),
            ValidationResult::Valid
        );
    }

    // -------------------------------------------------------
    // Valid — with breaking change indicator
    // -------------------------------------------------------

    #[test]
    fn valid_breaking_change_no_scope() {
        assert_eq!(
            validate_commit_message("feat!: remove deprecated endpoint"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_breaking_change_with_scope() {
        assert_eq!(
            validate_commit_message("feat(api)!: change response format"),
            ValidationResult::Valid
        );
    }

    // -------------------------------------------------------
    // Invalid messages
    // -------------------------------------------------------

    #[test]
    fn invalid_no_type() {
        assert_eq!(
            validate_commit_message("add new feature"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_unknown_type() {
        assert_eq!(
            validate_commit_message("feature: add login"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_missing_colon() {
        assert_eq!(
            validate_commit_message("feat add login"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_missing_space_after_colon() {
        assert_eq!(
            validate_commit_message("feat:add login"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_empty_description() {
        assert_eq!(
            validate_commit_message("feat: "),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_only_type_and_colon() {
        assert_eq!(
            validate_commit_message("feat:"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_uppercase_type() {
        assert_eq!(
            validate_commit_message("FEAT: add login"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_scope_with_spaces() {
        assert_eq!(
            validate_commit_message("feat(my scope): add login"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_empty_scope() {
        assert_eq!(
            validate_commit_message("feat(): add login"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_random_text() {
        assert_eq!(
            validate_commit_message("WIP"),
            ValidationResult::Invalid
        );
    }

    #[test]
    fn invalid_type_with_trailing_space_before_colon() {
        assert_eq!(
            validate_commit_message("feat : add login"),
            ValidationResult::Invalid
        );
    }

    // -------------------------------------------------------
    // Edge cases
    // -------------------------------------------------------

    #[test]
    fn valid_single_char_description() {
        assert_eq!(
            validate_commit_message("fix: x"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn valid_message_with_body_after_newline() {
        // Only the first line matters for validation
        assert_eq!(
            validate_commit_message("feat: add login\n\nThis adds a new login page."),
            ValidationResult::Valid
        );
    }

    #[test]
    fn leading_whitespace_is_trimmed() {
        assert_eq!(
            validate_commit_message("  feat: add login"),
            ValidationResult::Valid
        );
    }

    #[test]
    fn trailing_whitespace_is_trimmed() {
        assert_eq!(
            validate_commit_message("feat: add login   "),
            ValidationResult::Valid
        );
    }
}
