use std::cell::RefCell;

use ruin_ui::{Color, TextFontFamily, TextWrap};

use crate::primitives::FontWeight;

/// Partially-specified text style for context-based inheritance.
///
/// Set on a container via `.text_style(...)`. All descendant `text()` nodes inherit
/// any fields that are `Some`, falling back to their own defaults for fields that are `None`.
/// Inner (closer) ancestors take precedence over outer ancestors.
#[derive(Clone, Default)]
pub struct PartialTextStyle {
    pub size: Option<f32>,
    pub color: Option<Color>,
    pub weight: Option<FontWeight>,
    pub font_family: Option<TextFontFamily>,
    pub wrap: Option<TextWrap>,
    pub line_height: Option<f32>,
}

impl PartialTextStyle {
    /// Merge `other` on top of `self` — `other`'s `Some` fields win.
    fn merge_over(self, other: &PartialTextStyle) -> Self {
        Self {
            size: other.size.or(self.size),
            color: other.color.or(self.color),
            weight: other.weight.or(self.weight),
            font_family: other.font_family.clone().or(self.font_family),
            wrap: other.wrap.or(self.wrap),
            line_height: other.line_height.or(self.line_height),
        }
    }
}

thread_local! {
    static TEXT_STYLE_STACK: RefCell<Vec<PartialTextStyle>> = const { RefCell::new(Vec::new()) };
}

/// Push a partial style onto the context stack. Must be paired with `pop_text_style`.
pub(crate) fn push_text_style(style: PartialTextStyle) {
    TEXT_STYLE_STACK.with(|stack| stack.borrow_mut().push(style));
}

/// Pop the top partial style from the context stack.
pub(crate) fn pop_text_style() {
    TEXT_STYLE_STACK.with(|stack| {
        stack.borrow_mut().pop();
    });
}

/// Returns the effective `PartialTextStyle` at the current render position by merging
/// all layers from outermost to innermost (innermost wins).
pub(crate) fn current_text_style() -> PartialTextStyle {
    TEXT_STYLE_STACK.with(|stack| {
        stack
            .borrow()
            .iter()
            .fold(PartialTextStyle::default(), |acc, layer| {
                acc.merge_over(layer)
            })
    })
}
