use nalgebra_glm::{Vec2, Vec4};

use crate::ecs::primitives::EasingFunction;
use crate::ecs::text::components::{TextAlignment, VerticalAlignment};
use crate::ecs::ui::components::{
    AutoSizeMode, DragPayload, StateTransition, ThemeColor, UiAnimationPhase, UiAnimationType,
    UiDepthMode, UiDragSource, UiDropTarget, UiLayoutNode, UiLayoutRoot, UiNodeAnimation,
    UiNodeColor, UiNodeContent, UiNodeInteraction, UiStateWeights, UiThemeBinding,
};
use crate::ecs::ui::layout_types::{
    BoundaryLayout, FlowAlignment, FlowDirection, FlowLayout, GridLayout, ScalingMode, SolidLayout,
    UiLayoutType, WindowLayout,
};
use crate::ecs::ui::state::UiStateTrait;
use crate::ecs::ui::theme::UiTheme;
use crate::ecs::ui::types::Anchor;
use crate::ecs::ui::units::UiValue;
use crate::ecs::world::World;
use crate::render::wgpu::passes::geometry::UiLayer;

use crate::prelude::*;
pub struct UiTreeBuilder<'a> {
    world: &'a mut World,
    root: freecs::Entity,
    current_parent: freecs::Entity,
    parent_stack: Vec<freecs::Entity>,
}

impl<'a> UiTreeBuilder<'a> {
    pub fn new(world: &'a mut World) -> Self {
        let root = world.spawn();
        world.core.add_components(root, crate::ecs::world::PARENT);
        world
            .ui
            .add_components(root, crate::ecs::world::UI_LAYOUT_ROOT);

        if let Some(root_comp) = world.ui.get_ui_layout_root_mut(root) {
            *root_comp = UiLayoutRoot::default();
        }

        ui_mark_children_dirty(world);

        Self {
            world,
            root,
            current_parent: root,
            parent_stack: Vec::new(),
        }
    }

    pub fn from_parent(world: &'a mut World, parent_entity: freecs::Entity) -> Self {
        ui_mark_children_dirty(world);
        Self {
            world,
            root: parent_entity,
            current_parent: parent_entity,
            parent_stack: Vec::new(),
        }
    }

    pub fn finish_subtree(self) {}

    pub fn assign_tab_index(&mut self, entity: freecs::Entity) {
        let tab_index = self.world.resources.retained_ui.next_tab_index;
        if let Some(interaction) = self.world.ui.get_ui_node_interaction_mut(entity) {
            interaction.tab_index = Some(tab_index);
        }
        self.world.resources.retained_ui.next_tab_index += 1;
    }

    pub fn with_absolute_scale(self, scale: f32) -> Self {
        if let Some(root) = self.world.ui.get_ui_layout_root_mut(self.root) {
            root.absolute_scale = scale;
        }
        self
    }

    pub fn with_default_font_size(self, size: f32) -> Self {
        if let Some(root) = self.world.ui.get_ui_layout_root_mut(self.root) {
            root.default_font_size = size;
        }
        self
    }

    pub fn root_entity(&self) -> freecs::Entity {
        self.root
    }

    pub fn current_parent(&self) -> freecs::Entity {
        self.current_parent
    }

    pub fn world_mut(&mut self) -> &mut World {
        self.world
    }

    pub fn active_theme(&self) -> &UiTheme {
        self.world.resources.retained_ui.theme_state.active_theme()
    }

    pub fn add_node(&mut self) -> UiNodeBuilder<'_, 'a> {
        let entity = self.world.spawn();
        self.world
            .core
            .add_components(entity, crate::ecs::world::PARENT);
        self.world.ui.add_components(
            entity,
            crate::ecs::world::UI_LAYOUT_NODE
                | crate::ecs::world::UI_NODE_COLOR
                | crate::ecs::world::UI_NODE_CONTENT
                | crate::ecs::world::UI_STATE_WEIGHTS,
        );

        if let Some(parent) = self.world.core.get_parent_mut(entity) {
            *parent = crate::ecs::transform::components::Parent(Some(self.current_parent));
        }
        if let Some(node) = self.world.ui.get_ui_layout_node_mut(entity) {
            *node = UiLayoutNode::default();
        }
        if let Some(color) = self.world.ui.get_ui_node_color_mut(entity) {
            *color = UiNodeColor::default();
        }
        if let Some(content) = self.world.ui.get_ui_node_content_mut(entity) {
            *content = UiNodeContent::default();
        }
        if let Some(weights) = self.world.ui.get_ui_state_weights_mut(entity) {
            *weights = UiStateWeights::default();
        }

        self.world.resources.transform_state.children_cache_valid = false;
        ui_mark_layout_dirty(self.world);

        UiNodeBuilder { tree: self, entity }
    }

    pub fn push_parent(&mut self, entity: freecs::Entity) {
        debug_assert!(
            entity != self.current_parent && !self.parent_stack.contains(&entity),
            "push_parent cycle detected: entity {:?} is already in the parent chain",
            entity
        );
        self.parent_stack.push(self.current_parent);
        self.current_parent = entity;
    }

    pub fn pop_parent(&mut self) {
        if let Some(parent) = self.parent_stack.pop() {
            self.current_parent = parent;
        }
    }

    pub fn within_content(&mut self, entity: freecs::Entity, f: impl FnOnce(&mut UiTreeBuilder)) {
        let content = ui_widget_content(self.world, entity).unwrap_or(entity);
        self.push_parent(content);
        f(self);
        self.pop_parent();
    }

    /// Run a closure with `entity` as the active parent, restoring the previous
    /// parent automatically. Replaces manual `push_parent` / `pop_parent` pairs
    /// that are easy to mismatch.
    pub fn in_parent<R>(&mut self, entity: freecs::Entity, f: impl FnOnce(&mut Self) -> R) -> R {
        self.push_parent(entity);
        let result = f(self);
        self.pop_parent();
        result
    }

    /// Build a horizontal "label + slot" row: a static text label of fixed
    /// `label_width` followed by a body slot the closure populates. Returns
    /// whatever the closure returns (typically the slot widget entity).
    /// Defaults to 32px height; size the row externally if you need taller.
    pub fn label_row<R>(
        &mut self,
        label: &str,
        label_width: f32,
        body: impl FnOnce(&mut Self) -> R,
    ) -> R {
        use crate::ecs::ui::layout_types::FlowAlignment;
        let row = self
            .add_node()
            .size(
                crate::ecs::ui::units::Length::Pct(100.0),
                crate::ecs::ui::units::Length::Px(32.0),
            )
            .flow_horizontal()
            .gap(8.0)
            .align_cross(FlowAlignment::Center)
            .entity();
        self.in_parent(row, |tree| {
            tree.add_node()
                .size(
                    crate::ecs::ui::units::Length::Px(label_width),
                    crate::ecs::ui::units::Length::Px(18.0),
                )
                .label(label, 14.0, ThemeColor::Text)
                .entity();
            body(tree)
        })
    }

    /// RAII guard variant of [`push_parent`]. The current parent is restored
    /// when the returned scope is dropped, so apps can append children across
    /// multiple statements without remembering to call `pop_parent`.
    pub fn children_of<'b>(&'b mut self, entity: freecs::Entity) -> ParentScope<'b, 'a> {
        ParentScope::push(self, entity)
    }

    /// RAII guard variant that targets a widget's content entity (for panels,
    /// scroll areas, etc). Same drop-on-scope-exit semantics as
    /// [`children_of`].
    pub fn children_of_widget<'b>(&'b mut self, entity: freecs::Entity) -> ParentScope<'b, 'a> {
        let content = ui_widget_content(self.world, entity).unwrap_or(entity);
        ParentScope::push(self, content)
    }

    pub fn finish(self) -> freecs::Entity {
        #[cfg(debug_assertions)]
        self.validate_layout();
        self.root
    }

    #[cfg(debug_assertions)]
    fn validate_layout(&self) {
        let children: Vec<freecs::Entity> = self
            .world
            .resources
            .transform_state
            .children_cache
            .get(&self.root)
            .map(|v| v.to_vec())
            .unwrap_or_default();

        let root_has_flow = self
            .world
            .ui
            .get_ui_layout_node(self.root)
            .is_some_and(|n| n.flow_layout.is_some());

        for child in &children {
            if let Some(node) = self.world.ui.get_ui_layout_node(*child)
                && !root_has_flow
                && let Some(ref child_size) = node.flow_child_size
            {
                let has_relative = child_size.relative.is_some()
                    || child_size.relative_width.is_some()
                    || child_size.relative_height.is_some();
                if has_relative {
                    eprintln!(
                        "UI layout warning: entity {:?} has flow_child_size with relative units but parent {:?} has no flow layout",
                        child, self.root
                    );
                }
            }
        }
    }
}

pub struct UiNodeBuilder<'b, 'a> {
    tree: &'b mut UiTreeBuilder<'a>,
    entity: freecs::Entity,
}

impl<'b, 'a> UiNodeBuilder<'b, 'a> {
    /// Terminator that returns the constructed entity. Use this at the end of
    /// every node-builder chain.
    pub fn entity(self) -> freecs::Entity {
        self.entity
    }

    pub fn boundary(
        self,
        position_1: impl Into<UiValue<Vec2>>,
        position_2: impl Into<UiValue<Vec2>>,
    ) -> Self {
        let layout = UiLayoutType::Boundary(BoundaryLayout {
            position_1: position_1.into(),
            position_2: position_2.into(),
        });
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.base_layout = Some(layout);
        }
        self
    }

    pub fn window(
        self,
        position: impl Into<UiValue<Vec2>>,
        size: impl Into<UiValue<Vec2>>,
        anchor: Anchor,
    ) -> Self {
        let layout = UiLayoutType::Window(WindowLayout {
            position: position.into(),
            size: size.into(),
            anchor,
        });
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.base_layout = Some(layout);
        }
        self
    }

    pub fn solid(
        self,
        size: impl Into<UiValue<Vec2>>,
        scaling: ScalingMode,
        alignment: Vec2,
    ) -> Self {
        let layout = UiLayoutType::Solid(SolidLayout {
            size: size.into(),
            scaling,
            alignment,
        });
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.base_layout = Some(layout);
        }
        self
    }

    pub fn flow(self, direction: FlowDirection, padding: f32, spacing: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.flow_layout = Some(FlowLayout {
                direction,
                padding,
                spacing,
                alignment: FlowAlignment::Start,
                cross_alignment: FlowAlignment::Start,
                wrap: false,
            });
        }
        self
    }

    pub fn flow_with_alignment(
        self,
        direction: FlowDirection,
        padding: f32,
        spacing: f32,
        alignment: FlowAlignment,
        cross_alignment: FlowAlignment,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.flow_layout = Some(FlowLayout {
                direction,
                padding,
                spacing,
                alignment,
                cross_alignment,
                wrap: false,
            });
        }
        self
    }

    pub fn flow_wrap(self) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity)
            && let Some(flow) = &mut node.flow_layout
        {
            flow.wrap = true;
        }
        self
    }

    pub fn grid(
        self,
        columns: usize,
        row_height: f32,
        padding: f32,
        column_spacing: f32,
        row_spacing: f32,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.grid_layout = Some(GridLayout {
                columns,
                row_height,
                padding,
                column_spacing,
                row_spacing,
                min_column_width: None,
            });
        }
        self
    }

    pub fn auto_grid(self, min_column_width: f32, row_height: f32, spacing: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.grid_layout = Some(GridLayout {
                columns: 1,
                row_height,
                padding: spacing,
                column_spacing: spacing,
                row_spacing: spacing,
                min_column_width: Some(min_column_width),
            });
        }
        self
    }

    pub fn flow_child(self, size: impl Into<UiValue<Vec2>>) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.flow_child_size = Some(size.into());
        }
        self
    }

    /// Sets `flow_child_size` to a fixed pixel size in both axes.
    pub fn size_px(self, width: f32, height: f32) -> Self {
        self.flow_child(crate::ecs::ui::units::Ab(nalgebra_glm::vec2(width, height)))
    }

    /// Sets `flow_child_size` to fill the parent's width and contribute zero
    /// height. Containers auto-promote zero to AUTO; leaves stay at zero unless
    /// `auto_height` is also set.
    pub fn fill_width(self) -> Self {
        self.flow_child(crate::ecs::ui::units::Rl(nalgebra_glm::vec2(100.0, 0.0)))
    }

    /// Sets `flow_child_size` to fill the parent's height and contribute zero
    /// width.
    pub fn fill_height(self) -> Self {
        self.flow_child(crate::ecs::ui::units::Rl(nalgebra_glm::vec2(0.0, 100.0)))
    }

    /// Sets `flow_child_size` to 100% of the parent in both axes.
    pub fn fill(self) -> Self {
        self.flow_child(crate::ecs::ui::units::Rl(nalgebra_glm::vec2(100.0, 100.0)))
    }

    /// Marks the node as auto-height, so its height is measured from its
    /// content (and from wrap-aware text measurement for wrapping captions).
    pub fn auto_height(self) -> Self {
        self.auto_size(AutoSizeMode::Height)
    }

    /// Marks the node as auto-width.
    pub fn auto_width(self) -> Self {
        self.auto_size(AutoSizeMode::Width)
    }

    /// Sets `flow_child_size` from per-axis [`Length`] values.
    /// `node.size(100.pct(), 24.px())` is the canonical form for full-width
    /// fixed-height children. Pass [`Length::Auto`] in either axis to set
    /// auto-size on that axis (content-driven measurement).
    pub fn size(
        self,
        width: crate::ecs::ui::units::Length,
        height: crate::ecs::ui::units::Length,
    ) -> Self {
        use crate::ecs::ui::units::UiAxis;
        let mut value = UiValue::<Vec2>::default();
        if !width.is_auto() {
            value = value + width.into_axis(UiAxis::Horizontal);
        }
        if !height.is_auto() {
            value = value + height.into_axis(UiAxis::Vertical);
        }
        let auto = match (width.is_auto(), height.is_auto()) {
            (true, true) => Some(AutoSizeMode::Both),
            (true, false) => Some(AutoSizeMode::Width),
            (false, true) => Some(AutoSizeMode::Height),
            (false, false) => None,
        };
        let this = self.flow_child(value);
        match auto {
            Some(mode) => this.auto_size(mode),
            None => this,
        }
    }

    /// Configure the node as a vertical flow container with no padding or gap.
    /// Chain `.padding(p)` and `.gap(g)` to adjust.
    pub fn flow_vertical(self) -> Self {
        self.flow(FlowDirection::Vertical, 0.0, 0.0)
    }

    /// Configure the node as a horizontal flow container with no padding or
    /// gap.
    pub fn flow_horizontal(self) -> Self {
        self.flow(FlowDirection::Horizontal, 0.0, 0.0)
    }

    /// Set the padding on an existing flow container. No-op if the node is not
    /// a flow container.
    pub fn padding(self, padding: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity)
            && let Some(flow) = node.flow_layout.as_mut()
        {
            flow.padding = padding;
        }
        self
    }

    /// Set the gap (spacing between children) on an existing flow container.
    pub fn gap(self, spacing: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity)
            && let Some(flow) = node.flow_layout.as_mut()
        {
            flow.spacing = spacing;
        }
        self
    }

    /// Set the main-axis alignment of an existing flow container.
    pub fn align_main(self, alignment: FlowAlignment) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity)
            && let Some(flow) = node.flow_layout.as_mut()
        {
            flow.alignment = alignment;
        }
        self
    }

    /// Set the cross-axis alignment of an existing flow container.
    pub fn align_cross(self, alignment: FlowAlignment) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity)
            && let Some(flow) = node.flow_layout.as_mut()
        {
            flow.cross_alignment = alignment;
        }
        self
    }

    /// Theme-bound foreground (text/icon) color for the [`UiBase`] state.
    /// Equivalent to `.with_theme_color::<UiBase>(role)`.
    pub fn fg(self, role: ThemeColor) -> Self {
        self.with_theme_color::<crate::ecs::ui::state::UiBase>(role)
    }

    /// Theme-bound background color for the [`UiBase`] state.
    pub fn bg(self, role: ThemeColor) -> Self {
        self.with_theme_color::<crate::ecs::ui::state::UiBase>(role)
    }

    /// Theme-bound color for the hover state.
    pub fn fg_hover(self, role: ThemeColor) -> Self {
        self.with_theme_color::<crate::ecs::ui::state::UiHover>(role)
    }

    /// Theme-bound color for the pressed state.
    pub fn fg_pressed(self, role: ThemeColor) -> Self {
        self.with_theme_color::<crate::ecs::ui::state::UiPressed>(role)
    }

    /// Theme-bound color for the focused state.
    pub fn fg_focused(self, role: ThemeColor) -> Self {
        self.with_theme_color::<crate::ecs::ui::state::UiFocused>(role)
    }

    /// Static-text shortcut: equivalent to `.with_text(text, size).text_left()
    /// .fg(role)`. Use for non-interactive labels; for interactive text,
    /// compose explicitly so you can configure state colors and alignment.
    pub fn label(self, text: &str, size: f32, role: ThemeColor) -> Self {
        self.with_text(text, size).text_left().fg(role)
    }

    /// Rounded-rect background with no border (transparent border color).
    /// Equivalent to `.with_rect(corner, 0.0, vec4(0,0,0,0))`. Use
    /// `.with_rect(...)` directly when you need a border.
    pub fn rect(self, corner_radius: f32) -> Self {
        self.with_rect(corner_radius, 0.0, nalgebra_glm::vec4(0.0, 0.0, 0.0, 0.0))
    }

    /// Window-anchored at top-left. Equivalent to
    /// `.window(position, size, Anchor::TopLeft)`. Use the explicit `.window`
    /// form when you need a non-default anchor.
    pub fn window_at(
        self,
        position: impl Into<UiValue<Vec2>>,
        size: impl Into<UiValue<Vec2>>,
    ) -> Self {
        self.window(position, size, crate::ecs::ui::types::Anchor::TopLeft)
    }

    /// Apply the standard "card" composition: rounded panel rect with theme
    /// border, base panel color, and the panel effect role for shadow/glow.
    pub fn card(self) -> Self {
        let theme = self
            .tree
            .world
            .resources
            .retained_ui
            .theme_state
            .active_theme();
        let radius = theme.corner_radius;
        let border_color = theme.border_color;
        self.with_rect(radius, 1.0, border_color)
            .with_theme_border_color(ThemeColor::Border)
            .with_theme_color::<crate::ecs::ui::state::UiBase>(ThemeColor::Panel)
            .with_theme_effect_role(crate::ecs::ui::components::ThemeEffect::PanelEffect)
    }

    /// Register the entity in the named-entity registry so it can be looked up
    /// later via [`World::named_entity`](crate::ecs::world::World).
    pub fn named(self, name: &str) -> Self {
        self.tree
            .world
            .resources
            .retained_ui
            .accessibility
            .named_entities
            .insert(name.to_string(), self.entity);
        self
    }

    pub fn flex_grow(self, weight: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.flex_grow = Some(weight);
        }
        self
    }

    pub fn flex_shrink(self, factor: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.flex_shrink = Some(factor);
        }
        self
    }

    pub fn with_min_size(self, min: Vec2) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.min_size = Some(min);
        }
        self
    }

    pub fn with_max_size(self, max: Vec2) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.max_size = Some(max);
        }
        self
    }

    pub fn with_z_index(self, z: i32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.z_index = Some(z);
        }
        self
    }

    pub fn auto_size(self, mode: AutoSizeMode) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.auto_size = mode;
        }
        self
    }

    pub fn auto_size_padding(self, padding: Vec2) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.auto_size_padding = padding;
        }
        self
    }

    pub fn responsive_size_at(
        self,
        breakpoint: crate::ecs::ui::resources::UiBreakpoint,
        size: crate::ecs::ui::units::UiValue<Vec2>,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            let responsive = node
                .responsive
                .get_or_insert_with(crate::ecs::ui::components::UiResponsive::default);
            responsive.override_mut(breakpoint).size = Some(size);
        }
        self
    }

    pub fn responsive_position_1_at(
        self,
        breakpoint: crate::ecs::ui::resources::UiBreakpoint,
        position: crate::ecs::ui::units::UiValue<Vec2>,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            let responsive = node
                .responsive
                .get_or_insert_with(crate::ecs::ui::components::UiResponsive::default);
            responsive.override_mut(breakpoint).position_1 = Some(position);
        }
        self
    }

    pub fn responsive_position_2_at(
        self,
        breakpoint: crate::ecs::ui::resources::UiBreakpoint,
        position: crate::ecs::ui::units::UiValue<Vec2>,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            let responsive = node
                .responsive
                .get_or_insert_with(crate::ecs::ui::components::UiResponsive::default);
            responsive.override_mut(breakpoint).position_2 = Some(position);
        }
        self
    }

    pub fn responsive_visible_at(
        self,
        breakpoint: crate::ecs::ui::resources::UiBreakpoint,
        visible: bool,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            let responsive = node
                .responsive
                .get_or_insert_with(crate::ecs::ui::components::UiResponsive::default);
            responsive.override_mut(breakpoint).visible = Some(visible);
        }
        self
    }

    pub fn with_rect(self, corner_radius: f32, border_width: f32, border_color: Vec4) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
            *content = UiNodeContent::Rect {
                corner_radius,
                border_width,
                border_color,
            };
        }
        self
    }

    pub fn with_shadow(
        self,
        color: Vec4,
        offset: nalgebra_glm::Vec2,
        blur: f32,
        spread: f32,
    ) -> Self {
        self.tree
            .world
            .ui
            .add_components(self.entity, crate::ecs::world::UI_NODE_SHADOW);
        if let Some(shadow) = self.tree.world.ui.get_ui_node_shadow_mut(self.entity) {
            *shadow = crate::ecs::ui::components::UiNodeShadow {
                color,
                offset,
                blur,
                spread,
            };
        }
        self
    }

    pub fn with_text(self, text: &str, font_size: f32) -> Self {
        let text_slot = self.tree.world.resources.text.cache.add_text(text);
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
            *content = UiNodeContent::Text {
                text_slot,
                font_index: 0,
                font_size_override: Some(font_size),
                outline_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
                outline_width: 0.0,
                alignment: TextAlignment::Center,
                vertical_alignment: VerticalAlignment::Middle,
                overflow: crate::ecs::ui::components::TextOverflow::default(),
                monospace_width: None,
                font_kind: crate::ecs::text::resources::font_engine::FontKind::Default,
            };
        }
        self
    }

    pub fn with_text_slot(self, text_slot: usize, font_size: f32) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
            *content = UiNodeContent::Text {
                text_slot,
                font_index: 0,
                font_size_override: Some(font_size),
                outline_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
                outline_width: 0.0,
                alignment: TextAlignment::Center,
                vertical_alignment: VerticalAlignment::Middle,
                overflow: crate::ecs::ui::components::TextOverflow::default(),
                monospace_width: None,
                font_kind: crate::ecs::text::resources::font_engine::FontKind::Default,
            };
        }
        self
    }

    pub fn with_monospace_width(self, width: f32) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
            && let UiNodeContent::Text {
                monospace_width, ..
            } = content
        {
            *monospace_width = Some(width);
        }
        self
    }

    pub fn with_font_index(self, index: usize) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
            && let UiNodeContent::Text { font_index, .. } = content
        {
            *font_index = index;
        }
        self
    }

    pub fn with_icon(self, icon: crate::ecs::ui::icons::IconGlyph, font_size: f32) -> Self {
        let mut buf = [0u8; 4];
        let icon_str: &str = icon.codepoint.encode_utf8(&mut buf);
        let text_slot = self.tree.world.resources.text.cache.add_text(icon_str);
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
            *content = UiNodeContent::Text {
                text_slot,
                font_index: 0,
                font_size_override: Some(font_size),
                outline_color: Vec4::new(0.0, 0.0, 0.0, 0.0),
                outline_width: 0.0,
                alignment: TextAlignment::Center,
                vertical_alignment: VerticalAlignment::Middle,
                overflow: crate::ecs::ui::components::TextOverflow::default(),
                monospace_width: None,
                font_kind: icon.font_kind,
            };
        }
        self
    }

    /// Left-align text horizontally, middle vertically (the most common combo).
    pub fn text_left(self) -> Self {
        self.with_text_alignment(TextAlignment::Left, VerticalAlignment::Middle)
    }

    /// Center text horizontally, middle vertically.
    pub fn text_center(self) -> Self {
        self.with_text_alignment(TextAlignment::Center, VerticalAlignment::Middle)
    }

    /// Right-align text horizontally, middle vertically.
    pub fn text_right(self) -> Self {
        self.with_text_alignment(TextAlignment::Right, VerticalAlignment::Middle)
    }

    /// Configure the node as a boundary-positioned absolute fill of its parent
    /// minus `inset` pixels on every side.
    pub fn absolute_fill(self, inset: f32) -> Self {
        self.boundary(
            crate::ecs::ui::units::Ab(nalgebra_glm::vec2(inset, inset)),
            crate::ecs::ui::units::Ab(nalgebra_glm::vec2(-inset, -inset))
                + crate::ecs::ui::units::Rl(nalgebra_glm::vec2(100.0, 100.0)),
        )
    }

    pub fn with_text_alignment(
        self,
        text_alignment: TextAlignment,
        text_vertical_alignment: VerticalAlignment,
    ) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
            && let UiNodeContent::Text {
                alignment,
                vertical_alignment,
                ..
            } = content
        {
            *alignment = text_alignment;
            *vertical_alignment = text_vertical_alignment;
        }
        self
    }

    pub fn with_text_outline(self, color: Vec4, width: f32) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
            && let UiNodeContent::Text {
                outline_color,
                outline_width,
                ..
            } = content
        {
            *outline_color = color;
            *outline_width = width;
        }
        self
    }

    pub fn with_text_overflow(self, mode: crate::ecs::ui::components::TextOverflow) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
            && let UiNodeContent::Text { overflow, .. } = content
        {
            *overflow = mode;
        }
        self
    }

    pub fn with_text_wrap(self) -> Self {
        self.with_text_overflow(crate::ecs::ui::components::TextOverflow::Wrap)
    }

    pub fn with_image(self, texture_index: u32) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
            *content = UiNodeContent::Image {
                texture_index,
                uv_min: Vec2::new(0.0, 0.0),
                uv_max: Vec2::new(1.0, 1.0),
            };
        }
        self
    }

    pub fn with_image_uv(self, texture_index: u32, uv_min: Vec2, uv_max: Vec2) -> Self {
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity) {
            *content = UiNodeContent::Image {
                texture_index,
                uv_min,
                uv_max,
            };
        }
        self
    }

    /// Apply a raw [`Vec4`] color directly. The color does **not** track theme
    /// changes; for theme-aware coloring use [`fg`](Self::fg)/[`bg`](Self::bg)
    /// or [`with_theme_color`](Self::with_theme_color). The `_raw` suffix
    /// makes theme-broken usage stand out at the call site.
    pub fn color_raw<S: UiStateTrait>(self, color: Vec4) -> Self {
        if let Some(node_color) = self.tree.world.ui.get_ui_node_color_mut(self.entity) {
            node_color.colors[S::INDEX] = Some(color);
        }
        if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
            binding.color_roles[S::INDEX] = None;
        }
        self
    }

    pub fn with_theme_color<S: UiStateTrait>(self, role: ThemeColor) -> Self {
        let color = {
            let theme = self
                .tree
                .world
                .resources
                .retained_ui
                .theme_state
                .active_theme();
            role.resolve(theme)
        };
        if let Some(node_color) = self.tree.world.ui.get_ui_node_color_mut(self.entity) {
            node_color.colors[S::INDEX] = Some(color);
        }
        if self
            .tree
            .world
            .ui
            .get_ui_theme_binding(self.entity)
            .is_none()
        {
            self.tree
                .world
                .ui
                .set_ui_theme_binding(self.entity, UiThemeBinding::default());
        }
        if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
            binding.color_roles[S::INDEX] = Some(role);
        }
        self
    }

    pub fn with_theme_shadow_role<S: UiStateTrait>(
        self,
        role: crate::ecs::ui::components::ThemeShadow,
    ) -> Self {
        if self
            .tree
            .world
            .ui
            .get_ui_theme_binding(self.entity)
            .is_none()
        {
            self.tree
                .world
                .ui
                .set_ui_theme_binding(self.entity, UiThemeBinding::default());
        }
        if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
            binding.shadow_roles[S::INDEX] = Some(role);
        }
        self.tree.world.resources.retained_ui.theme_state.generation += 1;
        self
    }

    pub fn with_theme_effect_role(self, role: crate::ecs::ui::components::ThemeEffect) -> Self {
        if self
            .tree
            .world
            .ui
            .get_ui_theme_binding(self.entity)
            .is_none()
        {
            self.tree
                .world
                .ui
                .set_ui_theme_binding(self.entity, UiThemeBinding::default());
        }
        if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
            binding.effect_role = Some(role);
        }
        self.tree.world.resources.retained_ui.theme_state.generation += 1;
        self
    }

    pub fn with_effect(self, effect: crate::ecs::ui::theme::RectEffect) -> Self {
        let (kind, params) = effect.encode();
        self.tree
            .world
            .ui
            .add_components(self.entity, crate::ecs::world::UI_NODE_EFFECT);
        if let Some(comp) = self.tree.world.ui.get_ui_node_effect_mut(self.entity) {
            comp.kind = kind;
            comp.params = params;
        }
        self
    }

    pub fn with_theme_border_color(self, role: ThemeColor) -> Self {
        let color = {
            let theme = self
                .tree
                .world
                .resources
                .retained_ui
                .theme_state
                .active_theme();
            role.resolve(theme)
        };
        if let Some(content) = self.tree.world.ui.get_ui_node_content_mut(self.entity)
            && let UiNodeContent::Rect { border_color, .. } = content
        {
            *border_color = color;
        }
        if self
            .tree
            .world
            .ui
            .get_ui_theme_binding(self.entity)
            .is_none()
        {
            self.tree
                .world
                .ui
                .set_ui_theme_binding(self.entity, UiThemeBinding::default());
        }
        if let Some(binding) = self.tree.world.ui.get_ui_theme_binding_mut(self.entity) {
            binding.border_color_role = Some(role);
        }
        self
    }

    pub fn with_interaction(self) -> Self {
        self.tree
            .world
            .ui
            .set_ui_node_interaction(self.entity, UiNodeInteraction::default());
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.pointer_events = true;
        }
        self
    }

    /// Re-enable pointer-event hit testing on a node that has no interaction
    /// component. Rare; most apps want either no hit testing (default) or
    /// `.with_interaction()` (which already enables pointer events).
    pub fn with_pointer_events(self) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.pointer_events = true;
        }
        self
    }

    pub fn with_test_id(self, id: &str) -> Self {
        let this = if self
            .tree
            .world
            .ui
            .get_ui_node_interaction(self.entity)
            .is_none()
        {
            self.with_interaction()
        } else {
            self
        };
        if let Some(interaction) = this.tree.world.ui.get_ui_node_interaction_mut(this.entity) {
            interaction.test_id = Some(id.to_string());
        }
        this.tree
            .world
            .resources
            .retained_ui
            .accessibility
            .test_id_map
            .insert(id.to_string(), this.entity);
        this
    }

    pub fn with_cursor_icon(self, icon: winit::window::CursorIcon) -> Self {
        if let Some(interaction) = self.tree.world.ui.get_ui_node_interaction_mut(self.entity) {
            interaction.cursor_icon = Some(icon);
        }
        self
    }

    pub fn with_tooltip(self, text: &str) -> Self {
        let this = if self
            .tree
            .world
            .ui
            .get_ui_node_interaction(self.entity)
            .is_none()
        {
            self.with_interaction()
        } else {
            self
        };
        if let Some(interaction) = this.tree.world.ui.get_ui_node_interaction_mut(this.entity) {
            interaction.tooltip_text = Some(text.to_string());
        }
        this
    }

    pub fn with_tooltip_entity(self, tooltip_entity: freecs::Entity) -> Self {
        let this = if self
            .tree
            .world
            .ui
            .get_ui_node_interaction(self.entity)
            .is_none()
        {
            self.with_interaction()
        } else {
            self
        };
        if let Some(interaction) = this.tree.world.ui.get_ui_node_interaction_mut(this.entity) {
            interaction.tooltip_entity = Some(tooltip_entity);
        }
        this
    }

    pub fn with_tab_index(self, index: i32) -> Self {
        if let Some(interaction) = self.tree.world.ui.get_ui_node_interaction_mut(self.entity) {
            interaction.tab_index = Some(index);
        }
        self
    }

    pub fn with_transition<S: UiStateTrait>(self, enter_speed: f32, exit_speed: f32) -> Self {
        if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
            let mut transition = StateTransition::for_state::<S>();
            transition.enter_speed = enter_speed;
            transition.exit_speed = exit_speed;
            weights.transitions[S::INDEX] = Some(transition);
        }
        self
    }

    pub fn with_eased_transition<S: UiStateTrait>(
        self,
        enter_speed: f32,
        exit_speed: f32,
        easing: EasingFunction,
    ) -> Self {
        if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
            weights.transitions[S::INDEX] = Some(StateTransition {
                enter_speed,
                exit_speed,
                easing,
                spring: None,
            });
        }
        self
    }

    pub fn with_spring_transition<S: UiStateTrait>(
        self,
        spring: crate::ecs::ui::components::Spring,
    ) -> Self {
        if let Some(weights) = self.tree.world.ui.get_ui_state_weights_mut(self.entity) {
            let mut transition = StateTransition::for_state::<S>();
            transition.spring = Some(spring);
            weights.transitions[S::INDEX] = Some(transition);
        }
        self
    }

    pub fn with_state_offset<S: UiStateTrait>(self, offset: nalgebra_glm::Vec2) -> Self {
        self.tree
            .world
            .ui
            .add_components(self.entity, crate::ecs::world::UI_NODE_TRANSFORM_STATES);
        if let Some(states) = self
            .tree
            .world
            .ui
            .get_ui_node_transform_states_mut(self.entity)
        {
            states.offsets[S::INDEX] = Some(offset);
        }
        self
    }

    pub fn with_state_scale<S: UiStateTrait>(self, scale: f32) -> Self {
        self.tree
            .world
            .ui
            .add_components(self.entity, crate::ecs::world::UI_NODE_TRANSFORM_STATES);
        if let Some(states) = self
            .tree
            .world
            .ui
            .get_ui_node_transform_states_mut(self.entity)
        {
            states.scales[S::INDEX] = Some(scale);
        }
        self
    }

    pub fn with_state_radius<S: UiStateTrait>(self, radius: f32) -> Self {
        self.tree
            .world
            .ui
            .add_components(self.entity, crate::ecs::world::UI_NODE_TRANSFORM_STATES);
        if let Some(states) = self
            .tree
            .world
            .ui
            .get_ui_node_transform_states_mut(self.entity)
        {
            states.radii[S::INDEX] = Some(radius);
        }
        self
    }

    pub fn with_depth(self, depth: UiDepthMode) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.depth = depth;
        }

        self
    }

    pub fn with_font_size(self, size: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.font_size = Some(size);
        }
        self
    }

    pub fn with_clip(self) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.clip_content = true;
        }
        self
    }

    pub fn with_visible(self, visible: bool) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.visible = visible;
        }

        self
    }

    pub fn without_pointer_events(self) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.pointer_events = false;
        }
        self
    }

    pub fn with_intro(self, animation: UiAnimationType, duration: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            let anim = node.animation.get_or_insert(UiNodeAnimation {
                intro: None,
                outro: None,
                duration,
                progress: 0.0,
                phase: UiAnimationPhase::Idle,
            });
            anim.intro = Some(animation);
            anim.duration = duration;
            if node.visible {
                anim.phase = UiAnimationPhase::IntroPlaying;
                anim.progress = 0.0;
            }
        }
        self
    }

    pub fn with_outro(self, animation: UiAnimationType, duration: f32) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            let anim = node.animation.get_or_insert(UiNodeAnimation {
                intro: None,
                outro: None,
                duration,
                progress: 0.0,
                phase: UiAnimationPhase::Idle,
            });
            anim.outro = Some(animation);
            anim.duration = duration;
        }
        self
    }

    pub fn with_drag_source(self, payload: DragPayload) -> Self {
        let this = if self
            .tree
            .world
            .ui
            .get_ui_node_interaction(self.entity)
            .is_none()
        {
            self.with_interaction()
        } else {
            self
        };
        this.tree
            .world
            .ui
            .set_ui_drag_source(this.entity, UiDragSource { payload });
        this
    }

    pub fn with_drop_target(self) -> Self {
        let this = if self
            .tree
            .world
            .ui
            .get_ui_node_interaction(self.entity)
            .is_none()
        {
            self.with_interaction()
        } else {
            self
        };
        this.tree
            .world
            .ui
            .set_ui_drop_target(this.entity, UiDropTarget::default());
        this
    }

    pub fn with_filtered_drop_target(
        self,
        filter: crate::ecs::ui::components::DragAcceptFilter,
    ) -> Self {
        let this = if self
            .tree
            .world
            .ui
            .get_ui_node_interaction(self.entity)
            .is_none()
        {
            self.with_interaction()
        } else {
            self
        };
        this.tree.world.ui.set_ui_drop_target(
            this.entity,
            UiDropTarget {
                accepted: true,
                filter,
            },
        );
        this
    }

    pub fn with_layer(self, layer: UiLayer) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.layer = Some(layer);
        }
        self
    }

    pub fn with_children(self, child_builder: impl FnOnce(&mut UiTreeBuilder<'a>)) -> Self {
        let entity = self.entity;
        let tree = self.tree;
        tree.push_parent(entity);
        child_builder(tree);
        tree.pop_parent();
        UiNodeBuilder { tree, entity }
    }

    pub fn with_responsive_flow(
        self,
        max_width: f32,
        direction: crate::ecs::ui::layout_types::FlowDirection,
    ) -> Self {
        if let Some(node) = self.tree.world.ui.get_ui_layout_node_mut(self.entity) {
            node.responsive_flow = Some(crate::ecs::ui::layout_types::ResponsiveFlowOverride {
                max_width,
                direction,
            });
        }
        self
    }
}

/// RAII guard returned by [`UiTreeBuilder::children_of`] /
/// [`UiTreeBuilder::children_of_widget`]. Holds a mutable borrow on the
/// builder while it is alive and pops the pushed parent on drop. Deref to the
/// underlying builder to call any of its methods.
///
/// ```ignore
/// let panel = tree.add_docked_panel_left("Side", 220.0);
/// let mut p = tree.children_of_widget(panel);
/// p.add_label("first");
/// p.add_label("second");
/// // parent restored when `p` goes out of scope
/// ```
pub struct ParentScope<'b, 'a: 'b> {
    tree: &'b mut UiTreeBuilder<'a>,
}

impl<'b, 'a: 'b> ParentScope<'b, 'a> {
    pub(crate) fn push(tree: &'b mut UiTreeBuilder<'a>, parent: freecs::Entity) -> Self {
        tree.push_parent(parent);
        Self { tree }
    }
}

impl<'b, 'a: 'b> std::ops::Deref for ParentScope<'b, 'a> {
    type Target = UiTreeBuilder<'a>;
    fn deref(&self) -> &Self::Target {
        self.tree
    }
}

impl<'b, 'a: 'b> std::ops::DerefMut for ParentScope<'b, 'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.tree
    }
}

impl Drop for ParentScope<'_, '_> {
    fn drop(&mut self) {
        self.tree.pop_parent();
    }
}
