//! Handlers for subject and predicate discovery endpoints.
//!
//! These endpoints scan existing Redb indexes to expose the subjects
//! and predicates known to the system, enabling autocomplete/typeahead
//! in the dashboard.

use axum::{
    extract::{Path, State},
    Json,
};
use tracing::instrument;

use crate::{
    dto::subjects::{ListPredicatesResponse, ListSubjectsParams, ListSubjectsResponse},
    error::Result,
    extractors::QsQuery,
    state::AppState,
};

use stemedb_storage::{key_codec, KVStore};

/// List all known subjects, with optional prefix filtering.
///
/// Scans the `\x00SUBJECTS:` index in Redb. Supports prefix filtering
/// via the `q` parameter for typeahead/autocomplete use cases.
#[utoipa::path(
    get,
    path = "/v1/subjects",
    params(
        ("q" = Option<String>, Query, description = "Prefix filter for subject names"),
        ("limit" = Option<usize>, Query, description = "Max results (default 100, max 1000)")
    ),
    responses(
        (status = 200, description = "List of subjects", body = ListSubjectsResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse)
    ),
    tag = "discovery"
)]
#[instrument(skip(state), fields(q = ?params.q, limit = ?params.limit))]
pub async fn list_subjects(
    State(state): State<AppState>,
    QsQuery(params): QsQuery<ListSubjectsParams>,
) -> Result<Json<ListSubjectsResponse>> {
    metrics::counter!("stemedb_queries_total", "endpoint" => "list_subjects").increment(1);

    let prefix = if let Some(ref q) = params.q {
        key_codec::subjects_index_key(q)
    } else {
        key_codec::subjects_scan_prefix()
    };

    let entries = state.store.scan_prefix(&prefix).await?;
    let total_count = entries.len();
    let limit = params.limit.unwrap_or(100).min(1000);

    let subjects: Vec<String> = entries
        .iter()
        .filter_map(|(k, _)| key_codec::extract_subject_from_subjects_key(k))
        .take(limit)
        .collect();

    Ok(Json(ListSubjectsResponse { subjects, total_count }))
}

/// List all predicates for a given subject.
///
/// Scans the `{subject}\x00SP:` index in Redb to find all predicates
/// that have been asserted for this subject.
#[utoipa::path(
    get,
    path = "/v1/subjects/{subject}/predicates",
    params(
        ("subject" = String, Path, description = "The subject to list predicates for")
    ),
    responses(
        (status = 200, description = "List of predicates for the subject", body = ListPredicatesResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse)
    ),
    tag = "discovery"
)]
#[instrument(skip(state), fields(%subject))]
pub async fn list_predicates(
    State(state): State<AppState>,
    Path(subject): Path<String>,
) -> Result<Json<ListPredicatesResponse>> {
    metrics::counter!("stemedb_queries_total", "endpoint" => "list_predicates").increment(1);

    let prefix = key_codec::subject_predicate_scan_prefix(&subject);
    let entries = state.store.scan_prefix(&prefix).await?;

    let predicates: Vec<String> =
        entries.iter().filter_map(|(k, _)| key_codec::extract_sp_key(k).map(|(_, p)| p)).collect();

    Ok(Json(ListPredicatesResponse { subject, predicates }))
}
