use crate::{CliError, Session};
use btctax_core::conventions::TRANSITION_DATE;
use btctax_core::persistence::load_all;
use btctax_core::tranche_guard::{in_force_allocation_exists, pre2025_tranche_exists};
use btctax_core::{EventId, EventPayload, LedgerEvent, Sat, TaxDate, WalletId};
use btctax_store::Passphrase;
use std::path::Path;
use time::OffsetDateTime;
const ALLOCATION_IS_FINAL_HINT: &str = "revisit the in-app safe-harbor allocation; if your filed \
allocation is already final, unallocated pre-2025 units are a facts-and-circumstances matter for a \
professional";
const TRANCHE_IS_FINAL_HINT: &str = "Void the tranche first (`reconcile void <decision-ref>`); if you \
have already filed the tranche's basis ($0 or a promoted floor), unallocated pre-2025 units are a \
facts-and-circumstances matter for a professional";
pub fn wallet_is_known(events: &[LedgerEvent], wallet: &WalletId) -> bool {
events.iter().any(|e| {
e.wallet.as_ref() == Some(wallet)
|| matches!(&e.payload, EventPayload::DeclareTranche(t) if &t.wallet == wallet)
})
}
pub fn guard_allocation_vs_tranche(events: &[LedgerEvent]) -> Result<(), CliError> {
if pre2025_tranche_exists(events) {
return Err(CliError::Usage(format!(
"refusing to record a safe-harbor allocation while a pre-2025 conservative-filing tranche \
($0 or a promoted floor, EstimatedConservative) is on file — v1 makes the two mutually \
exclusive. {TRANCHE_IS_FINAL_HINT}."
)));
}
Ok(())
}
pub(crate) fn guard_tranche_vs_allocation(
events: &[LedgerEvent],
window_end: TaxDate,
) -> Result<(), CliError> {
if window_end < TRANSITION_DATE && in_force_allocation_exists(events) {
return Err(CliError::Usage(format!(
"refusing to record a pre-2025 conservative-filing tranche while a safe-harbor allocation \
is on file — v1 makes the two mutually exclusive; {ALLOCATION_IS_FINAL_HINT}."
)));
}
Ok(())
}
pub fn declare_tranche(
vault_path: &Path,
pp: &Passphrase,
sat: Sat,
wallet: WalletId,
window_start: TaxDate,
window_end: TaxDate,
now: OffsetDateTime,
) -> Result<EventId, CliError> {
let mut session = Session::open(vault_path, pp)?;
let events = load_all(session.conn())?;
let cfg = session.config()?.to_projection();
let plan = crate::chokepoint::plan_declare(
&events,
session.prices(),
&cfg,
sat,
wallet.clone(),
window_start,
window_end,
None,
now,
)
.map_err(CliError::from)?;
if !wallet_is_known(&events, &wallet) {
eprintln!(
"warning: --wallet {} has no prior events in this vault; if this is a typo the conservative \
tranche lot is stranded in a phantom wallet (it still files at its conservative basis — $0, \
or a promoted floor). Re-run with the intended --wallet if this was unintended.",
crate::render::wallet_label(&wallet)
);
}
let event_id = crate::chokepoint::apply_declare(&mut session, plan, now)?;
eprint!("\n⚠ {}", btctax_core::experimental::NOTICE.plain_text());
Ok(event_id)
}