use axum::Json;
use axum::extract::{Path, Query, State};
use serde::Deserialize;
use vti_common::auth::extractor::AuthClaims;
use vti_common::error::AppError;
use vti_common::pagination::{Cursor, Paginated};
use crate::acl::get_acl_entry;
use crate::members::get_member;
use crate::relationships::{Relationship, list_for_did};
use crate::server::AppState;
const MAX_LIMIT: usize = 200;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListQuery {
pub cursor: Option<String>,
pub limit: Option<usize>,
}
pub async fn list(
_auth: AuthClaims,
State(state): State<AppState>,
Path(did): Path<String>,
Query(query): Query<ListQuery>,
) -> Result<Json<Paginated<Relationship>>, AppError> {
let limit = query.limit.unwrap_or(50).clamp(1, MAX_LIMIT);
let audit_key = state
.audit_writer
.as_ref()
.ok_or_else(|| AppError::Internal("audit_writer not initialised".into()))?
.active_key()
.await?;
let cursor = query
.cursor
.as_deref()
.map(|c| Cursor::decode(c, &audit_key.key))
.transpose()
.map_err(|e| AppError::Validation(format!("invalid cursor: {e}")))?;
let page = list_for_did(
&state.relationships_ks,
&state.relationships_by_did_ks,
&audit_key,
&did,
cursor.as_ref(),
limit,
)
.await?;
let mut filtered: Vec<Relationship> = Vec::with_capacity(page.items.len());
for rel in page.items {
let other = if rel.issuer_did == did {
&rel.subject_did
} else {
&rel.issuer_did
};
let other_purged = get_acl_entry(&state.acl_ks, other).await?.is_none()
&& get_member(&state.members_ks, other).await?.is_none();
if !other_purged {
filtered.push(rel);
}
}
Ok(Json(Paginated {
items: filtered,
next_cursor: page.next_cursor,
total_estimate: page.total_estimate,
}))
}