//! Admin-only crawler observability + control endpoints.
//!
//! Mounted under `/api/v1/admin/crawler*`, cookie-only via `RequireAdmin`.
//! All control endpoints return 503 when the crawler daemon is disabled
//! (`AppState.crawler == None`). Reads compose the live in-process status
//! ([`crate::crawler::status`]) with DB-derived queue counts and the
//! session/browser flags.
//!
//! Split into four siblings for navigability — the surface area grew
//! past the point where keeping it in one file made review harder:
//! - [`status`] — SSE stream + composed status snapshot
//! - [`control`] — run / restart / session endpoints
//! - [`dead_jobs`] — dead-letter list + requeue
//! - [`backlog`] — pending-chapters and missing-covers backlog reads

mod backlog;
mod control;
mod dead_jobs;
mod history;
mod metrics;
mod status;

use axum::Router;

use crate::app::{AppState, CrawlerControl};
use crate::error::AppError;

pub fn routes() -> Router<AppState> {
    Router::new()
        .merge(status::routes())
        .merge(control::routes())
        .merge(dead_jobs::routes())
        .merge(backlog::routes())
        .merge(history::routes())
        .merge(metrics::routes())
}

/// Default page size for the backlog list endpoints.
pub(super) fn default_limit() -> i64 {
    50
}

/// Shared 503 helper: the daemon-only control + observability endpoints
/// gate on a running crawler daemon. Returns the same code and body across
/// every caller so the frontend can rely on `service_unavailable` rather
/// than each handler returning its own variant. Yields an owned `Arc` (the
/// control handle is swapped on a config reload).
pub(super) fn require_crawler(
    state: &AppState,
) -> Result<std::sync::Arc<CrawlerControl>, AppError> {
    state.crawler().ok_or_else(|| {
        AppError::ServiceUnavailable("crawler daemon is disabled".into())
    })
}
