use brk_error::{Error, OptionData, Result};
use brk_types::{BlockTimestamp, Date, Day1, Height, Timestamp};
use jiff::Timestamp as JiffTimestamp;
use vecdb::ReadableVec;
use crate::Query;
const MTP_TERMINAL_STREAK: usize = 11;
impl Query {
pub fn block_by_timestamp(&self, timestamp: Timestamp) -> Result<BlockTimestamp> {
let indexer = self.indexer();
let computer = self.computer();
if self.safe_lengths().height == Height::ZERO {
return Err(Error::NotFound("No blocks indexed".into()));
}
let tip: usize = self.height().into();
let target = timestamp;
let date = Date::from(target);
let day1 = Day1::try_from(date).unwrap_or_default();
let first_height_of_day = computer
.indexes
.day1
.first_height
.collect_one(day1)
.unwrap_or(Height::from(0usize));
let start: usize = usize::from(first_height_of_day).min(tip);
let mut ts_cursor = indexer.vecs.blocks.timestamp.cursor();
let mut best: Option<(usize, Timestamp)> = None;
let mut above_streak = 0usize;
for h in start..=tip {
let block_ts = ts_cursor.get(h).data()?;
if block_ts <= target {
if best.is_none_or(|(_, bts)| block_ts > bts) {
best = Some((h, block_ts));
}
above_streak = 0;
} else {
above_streak += 1;
if above_streak >= MTP_TERMINAL_STREAK {
break;
}
}
}
if best.is_none() && start > 0 {
let mut above_streak = 0usize;
for h in (0..start).rev() {
let block_ts = ts_cursor.get(h).data()?;
if block_ts <= target {
if best.is_none_or(|(_, bts)| block_ts > bts) {
best = Some((h, block_ts));
}
above_streak = 0;
} else {
above_streak += 1;
if above_streak >= MTP_TERMINAL_STREAK {
break;
}
}
}
}
let (best_height, best_ts) =
best.ok_or_else(|| Error::NotFound("No block at or before timestamp".into()))?;
let height = Height::from(best_height);
let blockhash = indexer.vecs.blocks.blockhash.collect_one(height).data()?;
let ts_secs: i64 = (*best_ts).into();
let iso_timestamp = JiffTimestamp::from_second(ts_secs)
.map(|t| t.strftime("%Y-%m-%dT%H:%M:%S%.3fZ").to_string())
.unwrap_or_else(|_| best_ts.to_string());
Ok(BlockTimestamp {
height,
hash: blockhash,
timestamp: iso_timestamp,
})
}
}