use crate::event::{EventPayload, LedgerEvent};
use crate::identity::EventId;
use crate::state::{Blocker, BlockerKind};
use std::collections::{BTreeMap, BTreeSet};
pub fn is_revocable_payload(payload: &EventPayload) -> bool {
matches!(
payload,
EventPayload::TransferLink(_)
| EventPayload::ReclassifyOutflow(_)
| EventPayload::ClassifyInbound(_)
| EventPayload::ManualFmv(_)
| EventPayload::ClassifyRaw(_)
| EventPayload::MethodElection(_)
| EventPayload::LotSelection(_)
| EventPayload::ReclassifyIncome(_)
| EventPayload::SelfTransferPassthrough(_)
| EventPayload::SafeHarborAllocation(_)
| EventPayload::DeclareTranche(_)
| EventPayload::PromoteTranche(_)
)
}
pub fn voidable_decisions<'a>(
events: &'a [LedgerEvent],
blockers: &[Blocker],
) -> Vec<&'a LedgerEvent> {
let voided: BTreeSet<EventId> = events
.iter()
.filter_map(|e| {
if let EventPayload::VoidDecisionEvent(v) = &e.payload {
Some(v.target_event_id.clone())
} else {
None
}
})
.collect();
let effective_alloc = |e: &LedgerEvent| {
matches!(e.payload, EventPayload::SafeHarborAllocation(_)) && {
let has = |k| {
blockers
.iter()
.any(|b| b.kind == k && b.event.as_ref() == Some(&e.id))
};
!has(BlockerKind::SafeHarborTimebar) && !has(BlockerKind::SafeHarborUnconservable)
}
};
let promote_count: BTreeMap<EventId, usize> = {
let mut m: BTreeMap<EventId, usize> = BTreeMap::new();
for e in events {
if let EventPayload::PromoteTranche(p) = &e.payload {
if !voided.contains(&e.id) {
*m.entry(p.target.clone()).or_insert(0) += 1;
}
}
}
m
};
let promoted_target = |e: &LedgerEvent| {
matches!(e.payload, EventPayload::DeclareTranche(_))
&& promote_count.get(&e.id).copied() == Some(1)
};
events
.iter()
.filter(|e| matches!(e.id, EventId::Decision { .. }))
.filter(|e| !voided.contains(&e.id))
.filter(|e| is_revocable_payload(&e.payload))
.filter(|e| !effective_alloc(e))
.filter(|e| !promoted_target(e))
.collect()
}