//! GET /admin/crawler/metrics      — per-type average durations + success
//! GET /admin/crawler/metrics/ops  — paginated recent timed-operations log
//!
//! Pure DB reads over the durable `crawl_metrics` table. Drive the "Metrics"
//! tab on the crawler dashboard. `days` windows the rows (`0`/absent = all).

use axum::extract::{Query, State};
use axum::routing::get;
use axum::{Json, Router};
use serde::{Deserialize, Serialize};

use crate::app::AppState;
use crate::auth::extractor::RequireAdmin;
use crate::domain::crawl_metrics::{MetricsBucket, OpRow, OpSummary};
use crate::error::AppResult;
use crate::repo;
use crate::repo::crawl_metrics::OpFilter;

use super::default_limit;
// Reuse the analysis handler's window + bucket helpers so all the metrics
// endpoints clamp `days` and resolve `bucket` identically.
use crate::api::admin::analysis::{resolve_bucket, window_since, SeriesParams, SeriesResponse};

pub(super) fn routes() -> Router<AppState> {
    Router::new()
        .route("/admin/crawler/metrics", get(summary))
        .route("/admin/crawler/metrics/ops", get(list_ops))
        .route("/admin/crawler/metrics/series", get(series))
}

async fn series(
    State(state): State<AppState>,
    _admin: RequireAdmin,
    Query(params): Query<SeriesParams>,
) -> AppResult<Json<SeriesResponse<MetricsBucket>>> {
    let bucket = resolve_bucket(params.days, params.bucket.as_deref())?;
    let buckets = repo::crawl_metrics::series(&state.db, bucket, window_since(params.days)).await?;
    Ok(Json(SeriesResponse { buckets }))
}

#[derive(Debug, Deserialize, Default)]
struct SummaryParams {
    #[serde(default)]
    days: i64,
}

#[derive(Debug, Serialize)]
struct SummaryResponse {
    summary: Vec<OpSummary>,
}

async fn summary(
    State(state): State<AppState>,
    _admin: RequireAdmin,
    Query(params): Query<SummaryParams>,
) -> AppResult<Json<SummaryResponse>> {
    let since = window_since(params.days);
    let summary = repo::crawl_metrics::summary(&state.db, since).await?;
    Ok(Json(SummaryResponse { summary }))
}

#[derive(Debug, Deserialize, Default)]
struct OpsParams {
    #[serde(default)]
    op: Option<String>,
    #[serde(default)]
    outcome: Option<String>,
    #[serde(default)]
    days: i64,
    #[serde(default = "default_limit")]
    limit: i64,
    #[serde(default)]
    offset: i64,
}

async fn list_ops(
    State(state): State<AppState>,
    _admin: RequireAdmin,
    Query(params): Query<OpsParams>,
) -> AppResult<Json<crate::api::pagination::PagedResponse<OpRow>>> {
    let limit = params.limit.clamp(1, 200);
    let offset = params.offset.max(0);
    let op = params.op.filter(|s| !s.trim().is_empty());
    let outcome = params.outcome.filter(|s| !s.trim().is_empty());
    let (items, total) = repo::crawl_metrics::list_ops(
        &state.db,
        OpFilter {
            op: op.as_deref(),
            outcome: outcome.as_deref(),
            since: window_since(params.days),
        },
        limit,
        offset,
    )
    .await?;
    Ok(Json(crate::api::pagination::PagedResponse::with_total(
        items, limit, offset, total,
    )))
}
