use std::cell::RefCell;

use super::element::Node;
use microformats_types::{DebugContext, PropertyValue};

#[cfg(feature = "debug_flow")]
thread_local! {
    pub(super) static DEBUG_CONTEXT: RefCell<Option<DebugContext>> = const { RefCell::new(None) };
}

/// Execute a function with a debug context active.
///
/// This function sets up thread-local storage for debug information and ensures
/// it's properly cleaned up when the function completes. It's used to make
/// debug context available to the parser hooks during parsing.
///
/// # Arguments
///
/// * `ctx` - The debug context to make available during execution
/// * `f` - The function to execute with the debug context
///
/// # Returns
///
/// The result of executing `f` with the debug context available.
pub fn with_debug_context<F, R>(ctx: DebugContext, f: F) -> (R, DebugContext)
where
    F: FnOnce() -> R,
{
    DEBUG_CONTEXT.with(|cell| {
        *cell.borrow_mut() = Some(ctx.clone());
        let result = f();
        let ctx_out = cell.borrow_mut().take().unwrap();
        (result, ctx_out)
    })
}

/// Get the currently active debug context.
///
/// Returns the debug context if one is active (i.e., if we're currently parsing
/// with debug tracking enabled), or None if debug mode is not active.
///
/// This is typically called by parser hooks to collect debug information
/// during the parsing process.
pub fn get_debug_context() -> Option<DebugContext> {
    DEBUG_CONTEXT.with(|cell| cell.borrow().clone())
}

/// A parser hook that collects debug information during parsing.
///
/// The DebugHook is automatically activated when debug tracking is enabled
/// (via `Parser::with_id_generation(true)`). It captures detailed information
/// about which HTML elements contributed to which microformat properties,
/// enabling comprehensive debugging of the parsing process.
pub struct DebugHook;

impl super::ParserHook for DebugHook {
    fn on_property_matched(&self, node: &Node, name: &str, _value: &PropertyValue) {
        if let Some(mut ctx) = get_debug_context() {
            if let Some(mf2_id) = node.attr("data-mf2-id") {
                ctx.property_sources
                    .push(microformats_types::PropertySourceRecord {
                        path: format!("properties.{}", name),
                        element_id: mf2_id.to_string(),
                        property_name: name.to_string(),
                    });
            }
        }
    }

    fn on_item_matched(&self, node: &Node, _item_type: &str) {
        if let Some(mut ctx) = get_debug_context() {
            let debug_info = node.capture_debug_info();
            let mf2_id = debug_info
                .mf2_id
                .clone()
                .unwrap_or_else(|| format!("anon-{}", ctx.elements.len()));
            ctx.elements.insert(mf2_id.clone(), debug_info);
        }
    }
}
