use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};

use crate::buffer::Buffer;
use crate::component::{Component, EventCx, LayoutCx, MeasureCx};
use crate::dirty::Dirty;
use crate::event::{Event, EventPhase};
use crate::geom::{Pos, Rect, Size};
use crate::layout::Constraint;
use crate::render::RenderCx;
use crate::style::{Style, TextAlign, TextTruncate, TextWrap};

/// A globally unique identifier for a node in the component tree.
///
/// `NodeId::ROOT` (value 0) is reserved for the root node. All other nodes
/// receive a monotonically increasing id from [`NodeId::new`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId(u64);

impl NodeId {
    /// Returns a new unique `NodeId`.
    pub fn new() -> Self {
        static NEXT: AtomicU64 = AtomicU64::new(1);
        Self(NEXT.fetch_add(1, Ordering::Relaxed))
    }

    /// The reserved root node id (value 0).
    pub const ROOT: NodeId = NodeId(0);
}

/// A node in the component tree.
///
/// Each `Node` owns a boxed [`Component`], tracks its layout [`Rect`],
/// dirty flags, parent link, and child nodes. The tree is mutable —
/// `Node::add_child` and the builder methods on container widgets grow
/// the tree at construction time.
pub struct Node {
    /// Unique identifier for this node.
    pub id: NodeId,
    /// The layout rectangle assigned to this node (interior-mutable for
    /// borrow-splitting during layout and render).
    pub rect: Cell<Rect>,
    /// Per-node dirty flags.
    pub dirty: Dirty,
    /// Parent node id, if any.
    pub parent: Option<NodeId>,
    /// The boxed component that owns the logic for this node.
    pub component: Box<dyn Component>,
    /// Child nodes in display order.
    pub children: Vec<Node>,
}

impl Node {
    /// Returns the current layout rectangle of this node.
    pub fn rect(&self) -> Rect {
        self.rect.get()
    }

    /// Updates the layout rectangle of this node.
    pub fn set_rect(&self, r: Rect) {
        self.rect.set(r);
    }

    /// Creates a new leaf node wrapping the given component.
    ///
    /// The node receives a fresh [`NodeId`] via [`NodeId::new`].
    pub fn new(component: impl Component + 'static) -> Self {
        Self {
            id: NodeId::new(),
            rect: Cell::new(Rect::default()),
            dirty: Dirty::NONE,
            parent: None,
            component: Box::new(component),
            children: Vec::new(),
        }
    }

    /// Creates the root node with [`NodeId::ROOT`].
    pub fn root(component: impl Component + 'static) -> Self {
        Self {
            id: NodeId::ROOT,
            rect: Cell::new(Rect::default()),
            dirty: Dirty::NONE,
            parent: None,
            component: Box::new(component),
            children: Vec::new(),
        }
    }

    /// Appends a child node at the end of the children list.
    pub fn add_child(&mut self, child: Node) {
        self.children.push(child);
    }

    /// Renders this subtree into `buffer`, clipping to this node's rect.
    pub fn render(&self, buffer: &mut Buffer, focused_id: Option<NodeId>) {
        self.render_with_clip(buffer, focused_id, None);
    }

    /// Renders this subtree with an optional clip rectangle.
    pub fn render_with_clip(
        &self,
        buffer: &mut Buffer,
        focused_id: Option<NodeId>,
        clip_rect: Option<Rect>,
    ) {
        self.render_with_clip_and_wrap(
            buffer,
            focused_id,
            clip_rect,
            TextWrap::None,
            TextTruncate::None,
            TextAlign::Left,
        );
    }

    /// Renders this subtree with full text-flow control.
    pub fn render_with_clip_and_wrap(
        &self,
        buffer: &mut Buffer,
        focused_id: Option<NodeId>,
        clip_rect: Option<Rect>,
        wrap: crate::style::TextWrap,
        truncate: crate::style::TextTruncate,
        align: crate::style::TextAlign,
    ) {
        self.render_inner(buffer, focused_id, clip_rect, wrap, truncate, align, None, None);
    }

    /// Renders this subtree, merging style values inherited from a parent.
    pub fn render_with_parent(
        &self,
        buffer: &mut Buffer,
        focused_id: Option<NodeId>,
        clip_rect: Option<Rect>,
        wrap: crate::style::TextWrap,
        truncate: crate::style::TextTruncate,
        align: crate::style::TextAlign,
        parent_style: Option<&crate::style::Style>,
    ) {
        self.render_inner(
            buffer,
            focused_id,
            clip_rect,
            wrap,
            truncate,
            align,
            None,
            parent_style,
        );
    }

    /// Core render entry-point.
    ///
    /// Resolves the effective style from the stylesheet, parent inheritance,
    /// and the component's own style, then calls `Component::render`.
    pub fn render_inner(
        &self,
        buffer: &mut Buffer,
        focused_id: Option<NodeId>,
        clip_rect: Option<Rect>,
        wrap: crate::style::TextWrap,
        truncate: crate::style::TextTruncate,
        align: crate::style::TextAlign,
        sheet: Option<&crate::style_parser::StyleSheet>,
        parent_style: Option<&Style>,
    ) {
        let mut style = self.component.style();
        if let Some(p) = parent_style {
            style = crate::style_parser::inherit_style(p, &style);
        }
        if let Some(s) = sheet {
            use crate::style_parser::WidgetState;
            // Build widget state for pseudo-class matching
            let focus_within = focused_id.map_or(false, |fid| {
                if fid == self.id { true }
                else {
                    let mut ids = Vec::new();
                    self.collect_focusable(&mut ids);
                    ids.contains(&fid)
                }
            });
            let state = WidgetState {
                focused: focused_id == Some(self.id),
                focus_within,
                ..WidgetState::default()
            };
            let r = s.resolve(
                self.component.type_name(),
                self.component.id(),
                self.component.class(),
                &state,
            );
            style = crate::style_parser::merge_styles(r, &style);
        }
        let mut cx = RenderCx::new(self.rect(), buffer, style);
        cx.focused_id = focused_id;
        cx.clip_rect = clip_rect;
        cx.wrap = wrap;
        cx.truncate = truncate;
        cx.align = align;
        self.component.render(&mut cx);
    }

    /// Dispatches an event to this node and (if not stopped) its children.
    ///
    /// Calls `Component::update` before `Component::event`.
    pub fn event(
        &mut self,
        event: &Event,
        global_dirty: &mut Dirty,
        quit: &mut bool,
        task_tx: Option<std::sync::mpsc::Sender<String>>,
    ) {
        let mut stopped = false;
        let mut cx = EventCx::with_task_sender(
            &mut self.dirty,
            global_dirty,
            quit,
            EventPhase::Target,
            &mut stopped,
            task_tx.clone(),
        );
        cx.current_node_id = self.id;
        self.component.update(&mut cx);
        self.component.event(event, &mut cx);
        if !stopped {
            for child in &mut self.children {
                child.event(event, global_dirty, quit, task_tx.clone());
            }
        }
    }

    /// Measures the intrinsic size of this node's subtree given `constraint`.
    pub fn measure(&self, constraint: Constraint) -> Size {
        let mut cx = MeasureCx { constraint };
        self.component.measure(constraint, &mut cx)
    }

    /// Recursively calls [`Component::mount`] on this node and its children.
    pub fn mount(
        &mut self,
        global_dirty: &mut Dirty,
        quit: &mut bool,
        task_tx: Option<std::sync::mpsc::Sender<String>>,
    ) {
        let mut stopped = false;
        let mut cx = EventCx::with_task_sender(
            &mut self.dirty,
            global_dirty,
            quit,
            EventPhase::Target,
            &mut stopped,
            task_tx.clone(),
        );
        cx.current_node_id = self.id;
        self.component.mount(&mut cx);
        for child in &mut self.children {
            child.mount(global_dirty, quit, task_tx.clone());
        }
    }

    /// Recursively draws border outlines and type-name labels for every node.
    ///
    /// The currently focused node is drawn with a double border; others use
    /// rounded borders. This is toggled by the `d` key in debug mode.
    pub fn debug_render(&self, buffer: &mut Buffer, focused_id: Option<NodeId>) {
        let type_name = self.component.type_name();
        let short = type_name.rsplit("::").next().unwrap_or(type_name);

        let border = if focused_id == Some(self.id) {
            crate::style::Border::Double
        } else {
            crate::style::Border::Rounded
        };
        let style = crate::style::Style::default().fg(crate::style::Color::Yellow);
        buffer.draw_border(self.rect(), border, &style);

        buffer.write_text(
            Pos {
                x: self.rect().x.saturating_add(2),
                y: self.rect().y,
            },
            self.rect(),
            short,
            &style,
        );

        self.component.for_each_child(&mut |child: &Node| {
            child.debug_render(buffer, focused_id);
        });
    }

    /// Recursively calls [`Component::unmount`] on children (reverse order),
    /// then on this node.
    pub fn unmount(
        &mut self,
        global_dirty: &mut Dirty,
        quit: &mut bool,
        task_tx: Option<std::sync::mpsc::Sender<String>>,
    ) {
        for child in &mut self.children {
            child.unmount(global_dirty, quit, task_tx.clone());
        }
        let mut stopped = false;
        let mut cx = EventCx::with_task_sender(
            &mut self.dirty,
            global_dirty,
            quit,
            EventPhase::Target,
            &mut stopped,
            task_tx,
        );
        self.component.unmount(&mut cx);
    }

    /// Runs layout for this subtree.
    ///
    /// Sets this node's rect, assigns parent links to direct children, then
    /// calls `Component::layout` so the component can size and position its
    /// children.
    pub fn layout(&mut self, rect: Rect) {
        self.set_rect(rect);
        for child in &mut self.children {
            child.parent = Some(self.id);
        }
        let mut cx = LayoutCx::new(&mut self.children);
        self.component.layout(rect, &mut cx);
    }

    /// Returns the path of node ids from `self` down to `target_id`, inclusive.
    ///
    /// Returns `None` if `target_id` is not in this subtree.
    pub fn find_path_to(&self, target_id: NodeId) -> Option<Vec<NodeId>> {
        if self.id == target_id {
            return Some(vec![self.id]);
        }
        let mut result: Option<Vec<NodeId>> = None;
        self.component.for_each_child(&mut |child: &Node| {
            if result.is_none() {
                if let Some(child_path) = child.find_path_to(target_id) {
                    let mut full = vec![self.id];
                    full.extend(child_path);
                    result = Some(full);
                }
            }
        });
        result
    }

    /// Returns `true` if this node or any descendant has [`Dirty::PAINT`] set.
    pub fn any_needs_paint(&self) -> bool {
        self.dirty.contains(Dirty::PAINT)
            || self.children.iter().any(|c| c.any_needs_paint())
    }

    /// Returns `true` if this node or any descendant has [`Dirty::LAYOUT`] set.
    pub fn any_needs_layout(&self) -> bool {
        self.dirty.contains(Dirty::LAYOUT)
            || self.children.iter().any(|c| c.any_needs_layout())
    }

    /// Resets all dirty flags on this node to [`Dirty::NONE`].
    ///
    /// Does not recurse into children — the caller is responsible for clearing
    /// the full tree if needed.
    pub fn clear_dirty(&mut self) {
        self.dirty = Dirty::NONE;
    }

    /// Collects ids of all focusable nodes in this subtree into `ids`.
    pub fn collect_focusable(&self, ids: &mut Vec<NodeId>) {
        if self.component.focusable() {
            ids.push(self.id);
        }
        self.component
            .for_each_child(&mut |child: &Node| child.collect_focusable(ids));
    }

    /// Performs a hit-test against the layout rects in this subtree.
    ///
    /// Returns the deepest node whose rect contains `pos`, or `None` if no
    /// node matches.
    pub fn hit_test(&self, pos: Pos) -> Option<NodeId> {
        let mut hit: Option<NodeId> = None;
        self.component.for_each_child(&mut |child: &Node| {
            if let Some(id) = child.hit_test(pos) {
                hit = Some(id);
            }
        });
        if hit.is_none() && self.rect().contains(pos) {
            hit = Some(self.id);
        }
        hit
    }

    /// Dispatches an event to a specific target node in this subtree.
    ///
    /// Used by the runtime for capture and bubble phases. Returns `true` if
    /// the target was found, `false` otherwise.
    pub fn send_event(
        &mut self,
        target_id: NodeId,
        event: &Event,
        global_dirty: &mut Dirty,
        quit: &mut bool,
        phase: EventPhase,
        stopped: &mut bool,
        task_tx: Option<std::sync::mpsc::Sender<String>>,
    ) -> bool {
        if *stopped {
            return true;
        }
        if self.id == target_id {
            let mut cx = EventCx::with_task_sender(
                &mut self.dirty,
                global_dirty,
                quit,
                phase,
                stopped,
                task_tx.clone(),
            );
            cx.current_node_id = self.id;
            self.component.event(event, &mut cx);
            return true;
        }
        let mut found = false;
        self.component.for_each_child_mut(&mut |child: &mut Node| {
            if !found && !*stopped {
                found = child.send_event(
                    target_id, event, global_dirty, quit, phase, stopped, task_tx.clone(),
                );
            }
        });
        found
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::widgets::Label;

    #[test]
    fn test_collect_focusable_simple() {
        let label = Node::new(Label::new("test"));
        let mut ids = Vec::new();
        label.collect_focusable(&mut ids);
        // Label.focusable() returns false
        assert!(ids.is_empty());
    }

    #[test]
    fn test_collect_focusable_tree() {
        // Column → [Label, Checkbox(opt)] → Checkbox is focusable, Label is not
        use crate::widgets::{Checkbox, Column};
        let column = Node::new(
            Column::new()
                .child(Label::new("not focusable"))
                .child(Checkbox::new("focusable"))
        );
        let mut ids = Vec::new();
        column.collect_focusable(&mut ids);
        assert_eq!(ids.len(), 1);
    }

    #[test]
    fn test_collect_focusable_block() {
        // Column → Block(Checkbox("inner"))
        use crate::widgets::{Block, Checkbox, Column};
        let column = Node::new(
            Column::new()
                .child(Block::new(Checkbox::new("inner")))
        );
        let mut ids = Vec::new();
        column.collect_focusable(&mut ids);
        assert_eq!(ids.len(), 1, "focusable inside Block should be found");
    }
}
