use std::io::Write;
use crate::core::Column as _;
use crate::sql::Fetcher as _;
use crate::tenancy::error::TenancyError;
use crate::tenancy::pools::TenantPools;
use crate::tenancy::Org;
use super::args::next_value;
pub(super) async fn audit_cleanup_cmd<W: Write + Send>(
pools: &TenantPools,
args: &[String],
w: &mut W,
) -> Result<(), TenancyError> {
let mut days: Option<i64> = None;
let mut keep_last: Option<i64> = None;
let mut tenant_slug: Option<String> = None;
let mut iter = args.iter();
while let Some(flag) = iter.next() {
match flag.as_str() {
"--days" => {
let raw = next_value(&mut iter, "--days")?;
days = Some(raw.parse::<i64>().map_err(|_| {
TenancyError::Validation(format!("--days expects an integer, got `{raw}`"))
})?);
}
"--keep-last" => {
let raw = next_value(&mut iter, "--keep-last")?;
keep_last = Some(raw.parse::<i64>().map_err(|_| {
TenancyError::Validation(format!(
"--keep-last expects an integer, got `{raw}`"
))
})?);
}
"--tenant" => {
tenant_slug = Some(next_value(&mut iter, "--tenant")?);
}
"--help" | "-h" => {
write_verb_help(w)?;
return Ok(());
}
other => {
return Err(TenancyError::Validation(format!(
"unknown flag `{other}` — run with --help for usage"
)))
}
}
}
match (days, keep_last) {
(None, None) => {
return Err(TenancyError::Validation(
"audit-cleanup requires --days <N> or --keep-last <N>".into(),
))
}
(Some(_), Some(_)) => {
return Err(TenancyError::Validation(
"--days and --keep-last are mutually exclusive".into(),
))
}
_ => {}
}
let orgs: Vec<Org> = if let Some(ref slug) = tenant_slug {
let found: Vec<Org> = Org::objects()
.where_(Org::slug.eq(slug.as_str()))
.fetch(pools.registry())
.await?;
if found.is_empty() {
return Err(TenancyError::Validation(format!(
"tenant `{slug}` not found"
)));
}
found
} else {
Org::objects()
.where_(Org::active.eq(true))
.fetch(pools.registry())
.await?
};
let total_orgs = orgs.len();
let mut total_deleted: u64 = 0;
for org in &orgs {
let tenant_pool = pools.pool_for_org(org).await?;
let pool = tenant_pool.pool();
let deleted = if let Some(n) = days {
crate::audit::cleanup_older_than(pool, n).await?
} else if let Some(n) = keep_last {
crate::audit::cleanup_keep_last_n(pool, n).await?
} else {
unreachable!()
};
writeln!(w, " tenant={} deleted={}", org.slug, deleted)?;
total_deleted += deleted;
}
writeln!(
w,
"audit-cleanup done: tenants={total_orgs} total_deleted={total_deleted}"
)?;
Ok(())
}
fn write_verb_help<W: Write>(w: &mut W) -> Result<(), TenancyError> {
writeln!(w, "audit-cleanup — remove old entries from each tenant's audit log")?;
writeln!(w)?;
writeln!(w, "USAGE:")?;
writeln!(
w,
" audit-cleanup --days <N> delete entries older than N days"
)?;
writeln!(
w,
" audit-cleanup --keep-last <N> keep N most recent entries per row"
)?;
writeln!(w)?;
writeln!(w, "OPTIONS:")?;
writeln!(
w,
" --tenant <slug> scope to one tenant (default: every active tenant)"
)?;
writeln!(w)?;
writeln!(w, "EXAMPLES:")?;
writeln!(
w,
" cargo run --bin manage -- audit-cleanup --days 90"
)?;
writeln!(
w,
" cargo run --bin manage -- audit-cleanup --keep-last 50"
)?;
writeln!(
w,
" cargo run --bin manage -- audit-cleanup --tenant acme --days 90"
)?;
Ok(())
}