use crate::component::{Component, EventCx, MeasureCx};
use crate::event::Event;
use crate::geom::{Rect, Size};
use crate::layout::Constraint;
use crate::node::Node;
use crate::render::RenderCx;
use crate::style::Style;
use crate::text::Text;

/// A tabbed container widget.
///
/// Each tab has a title and a child component. Use `←`/`→` to switch tabs.
/// Only the selected tab's child is rendered and receives events.
pub struct Tabs {
    /// (title, child node) pairs
    tabs: Vec<(Text, Node)>,
    /// Currently selected tab index
    selected: usize,
    /// Current layout rect — updated on each layout pass
    rect: Rect,
    /// Default container style
    style: Style,
    /// Tab header row style
    header_style: Style,
    /// Selected tab style
    selected_style: Style,
}

impl Tabs {
    /// Creates an empty tab container.
    pub fn new() -> Self {
        Self {
            tabs: Vec::new(),
            selected: 0,
            rect: Rect::default(),
            style: Style::default(),
            header_style: Style::default().bold(),
            selected_style: Style::default(),
        }
    }

    /// Adds a tab with the given title and child component.
    pub fn tab(mut self, title: impl Into<Text>, component: impl Component + 'static) -> Self {
        self.tabs.push((title.into(), Node::new(component)));
        self
    }

    /// Sets the container style.
    pub fn style(mut self, style: Style) -> Self {
        self.style = style;
        self
    }

    /// Sets the header row style.
    pub fn header_style(mut self, style: Style) -> Self {
        self.header_style = style;
        self
    }

    /// Sets the selected tab style.
    pub fn selected_style(mut self, style: Style) -> Self {
        self.selected_style = style;
        self
    }

    /// Returns the currently selected tab index.
    pub fn selected_index(&self) -> usize {
        self.selected
    }

    /// Sets the selected tab.
    pub fn set_selected(&mut self, index: usize, cx: &mut EventCx) {
        if index < self.tabs.len() {
            self.selected = index;
            cx.invalidate_paint();
        }
    }

    /// Returns the inner content area (below header + separator).
    fn content_rect(&self) -> Rect {
        let y = self.rect.y.saturating_add(2); // header + separator
        Rect {
            x: self.rect.x,
            y,
            width: self.rect.width,
            height: self.rect.height.saturating_sub(2),
        }
    }
}

impl Component for Tabs {
    fn render(&self, cx: &mut RenderCx) {
        if self.tabs.is_empty() {
            return;
        }

        // --- header row ---
        for (i, (title, _)) in self.tabs.iter().enumerate() {
            if i == self.selected {
                cx.set_style(self.selected_style.clone());
            } else {
                cx.set_style(self.header_style.clone());
            }
            cx.text(format!(" {} ", title.first_text()));
            if i < self.tabs.len() - 1 {
                cx.set_style(self.header_style.clone());
                cx.text("│");
            }
        }
        cx.line("");

        // --- separator ---
        cx.set_style(self.style.clone());
        let total_width: u16 = self.tabs.iter()
            .map(|(t, _)| t.max_width() + 2)
            .sum::<u16>()
            + (self.tabs.len().saturating_sub(1)) as u16; // separators
        cx.text("─".repeat(total_width as usize));
        cx.line("");

        // --- selected tab content ---
        if let Some((_, child)) = self.tabs.get(self.selected) {
            child.render_with_parent(cx.buffer, cx.focused_id, cx.clip_rect, cx.wrap, cx.truncate, cx.align, Some(&cx.style));
        }
    }

    fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
        if self.tabs.is_empty() {
            return Size { width: 0, height: 0 };
        }

        let child_size = self.tabs.get(self.selected)
            .map(|(_, child)| child.measure(constraint))
            .unwrap_or_default();

        Size {
            width: constraint.max.width,
            height: 2u16.saturating_add(child_size.height), // header + separator + content
        }
    }

    fn event(&mut self, event: &Event, cx: &mut EventCx) {
        if self.tabs.is_empty() {
            return;
        }

        if matches!(event, Event::Focus | Event::Blur | Event::Tick) {
            return;
        }

        // Handle tab-switching and global quit in Target or Bubble phase.
        // Skip Capture — the focused child needs the event first.
        if cx.phase() == crate::event::EventPhase::Capture {
            return;
        }
        if let Event::Key(key_event) = event {
            match &key_event.key {
                crate::event::Key::Left => {
                    if self.selected > 0 {
                        self.selected -= 1;
                    } else {
                        self.selected = self.tabs.len() - 1;
                    }
                    cx.invalidate_paint();
                    cx.dispatch(crate::event::Command::FocusPrev);
                    return;
                }
                crate::event::Key::Right => {
                    if self.selected + 1 < self.tabs.len() {
                        self.selected += 1;
                    } else {
                        self.selected = 0;
                    }
                    cx.invalidate_paint();
                    cx.dispatch(crate::event::Command::FocusNext);
                    return;
                }
                crate::event::Key::Char('q') => {
                    cx.quit();
                    return;
                }
                _ => {}
            }
        }
        // Handle Ctrl+C
        if let Event::Key(k) = event {
            if k.key == crate::event::Key::Char('c') && k.modifiers.ctrl {
                cx.quit();
            }
        }
    }

    fn layout(&mut self, rect: Rect, _cx: &mut crate::component::LayoutCx) {
        self.rect = rect;
        let content = self.content_rect();
        if let Some((_, child)) = self.tabs.get_mut(self.selected) {
            child.layout(content);
        }
    }

    fn for_each_child(&self, f: &mut dyn FnMut(&Node)) {
        if let Some((_, child)) = self.tabs.get(self.selected) {
            f(child);
        }
    }

    fn for_each_child_mut(&mut self, f: &mut dyn FnMut(&mut Node)) {
        if let Some((_, child)) = self.tabs.get_mut(self.selected) {
            f(child);
        }
    }

    fn focusable(&self) -> bool {
        false
    }

    fn style(&self) -> Style {
        self.style.clone()
    }
}
