pub mod dirty_tracker;
pub mod discussion_queue;
pub mod discussions;
pub mod issues;
pub mod merge_requests;
pub mod mr_diffs;
pub mod mr_discussions;
pub mod orchestrator;
pub mod storage;
pub(crate) mod surgical;

pub use discussions::{
    IngestDiscussionsResult, prefetch_issue_discussions, write_prefetched_issue_discussions,
};
pub use issues::{IngestIssuesResult, IssueForDiscussionSync, ingest_issues};
pub use merge_requests::{
    IngestMergeRequestsResult, MrForDiscussionSync, get_mrs_needing_discussion_sync,
    ingest_merge_requests,
};
pub use mr_discussions::{IngestMrDiscussionsResult, ingest_mr_discussions};
/// Format a set of named counters as a compact human-readable summary,
/// filtering out zero values and joining with middle-dot separators.
/// Returns `"nothing to update"` when all values are zero.
pub(crate) fn nonzero_summary(pairs: &[(&str, usize)]) -> String {
    let parts: Vec<String> = pairs
        .iter()
        .filter(|(_, v)| *v > 0)
        .map(|(k, v)| format!("{v} {k}"))
        .collect();
    if parts.is_empty() {
        "nothing to update".to_string()
    } else {
        parts.join(" \u{b7} ")
    }
}

pub use orchestrator::{
    DrainResult, IngestMrProjectResult, IngestProjectResult, ProgressCallback, ProgressEvent,
    ingest_project_issues, ingest_project_issues_with_progress, ingest_project_merge_requests,
    ingest_project_merge_requests_with_progress,
};
