#[derive(Clone, Debug)]
pub struct IterationBudget {
    pub remaining: usize,
    pub hard_limit: usize,
    pub grace_call: bool,
    pub consumed: usize,
}

impl IterationBudget {
    pub fn new(limit: usize) -> Self {
        Self {
            remaining: limit,
            hard_limit: limit,
            grace_call: true,
            consumed: 0,
        }
    }

    pub fn can_continue(&self) -> bool {
        self.remaining > 0 || (self.remaining == 0 && self.grace_call)
    }

    pub fn consume(&mut self) -> bool {
        if self.remaining > 0 {
            self.remaining -= 1;
            self.consumed += 1;
            true
        } else if self.grace_call {
            self.grace_call = false;
            self.consumed += 1;
            true
        } else {
            false
        }
    }

    pub fn exhaust(&mut self) {
        self.remaining = 0;
        self.grace_call = false;
    }

    pub const fn total_consumed(&self) -> usize {
        self.consumed
    }
}
