use oxc_str::Ident;
use oxc_syntax::node::NodeId;

#[derive(Debug)]
pub struct LabeledScope<'a> {
    pub name: Ident<'a>,
    pub used: bool,
    pub node_id: NodeId,
}

#[derive(Debug, Default)]
pub struct UnusedLabels<'a> {
    pub stack: Vec<LabeledScope<'a>>,
    pub labels: Vec<NodeId>,
}

impl<'a> UnusedLabels<'a> {
    pub fn add(&mut self, name: Ident<'a>, node_id: NodeId) {
        self.stack.push(LabeledScope { name, used: false, node_id });
    }

    pub fn reference(&mut self, name: Ident<'_>) {
        for scope in self.stack.iter_mut().rev() {
            if scope.name == name {
                scope.used = true;
                return;
            }
        }
    }

    pub fn mark_unused(&mut self) {
        debug_assert!(
            !self.stack.is_empty(),
            "mark_unused called with empty label stack - this indicates mismatched add/mark_unused calls"
        );

        if let Some(scope) = self.stack.pop()
            && !scope.used
        {
            self.labels.push(scope.node_id);
        }
    }

    #[cfg(debug_assertions)]
    pub fn assert_empty(&self) {
        debug_assert!(
            self.stack.is_empty(),
            "Label stack not empty at end of processing - {} labels remaining. This indicates mismatched add/mark_unused calls",
            self.stack.len()
        );
    }
}
