use crate::_private::NonExhaustive;
use crate::button::event::ButtonOutcome;
use crate::util::{block_size, revert_style};
use rat_event::util::{have_keyboard_enhancement, MouseFlags};
use rat_event::{ct_event, ConsumedEvent, HandleEvent, MouseOnly, Regular};
use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
use rat_reloc::{relocate_area, RelocatableState};
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::BlockExt;
use ratatui::style::Style;
use ratatui::text::Text;
#[cfg(feature = "unstable-widget-ref")]
use ratatui::widgets::StatefulWidgetRef;
use ratatui::widgets::{Block, StatefulWidget, Widget};
use std::thread;
use std::time::Duration;
#[derive(Debug, Default, Clone)]
pub struct Button<'a> {
    text: Text<'a>,
    style: Style,
    focus_style: Option<Style>,
    hover_style: Option<Style>,
    armed_style: Option<Style>,
    armed_delay: Option<Duration>,
    block: Option<Block<'a>>,
}
#[derive(Debug, Clone)]
pub struct ButtonStyle {
    pub style: Style,
    pub focus: Option<Style>,
    pub armed: Option<Style>,
    pub hover: Option<Style>,
    pub block: Option<Block<'static>>,
    pub armed_delay: Option<Duration>,
    pub non_exhaustive: NonExhaustive,
}
#[derive(Debug)]
pub struct ButtonState {
    pub area: Rect,
    pub inner: Rect,
    pub hover_enabled: bool,
    pub armed: bool,
    pub armed_delay: Option<Duration>,
    pub focus: FocusFlag,
    pub mouse: MouseFlags,
    pub non_exhaustive: NonExhaustive,
}
impl Default for ButtonStyle {
    fn default() -> Self {
        Self {
            style: Default::default(),
            focus: None,
            armed: None,
            hover: None,
            block: None,
            armed_delay: None,
            non_exhaustive: NonExhaustive,
        }
    }
}
impl<'a> Button<'a> {
    pub fn new(text: impl Into<Text<'a>>) -> Self {
        Self::default().text(text)
    }
    #[inline]
    pub fn styles_opt(self, styles: Option<ButtonStyle>) -> Self {
        if let Some(styles) = styles {
            self.styles(styles)
        } else {
            self
        }
    }
    #[inline]
    pub fn styles(mut self, styles: ButtonStyle) -> Self {
        self.style = styles.style;
        if styles.focus.is_some() {
            self.focus_style = styles.focus;
        }
        if styles.armed.is_some() {
            self.armed_style = styles.armed;
        }
        if styles.armed_delay.is_some() {
            self.armed_delay = styles.armed_delay;
        }
        if styles.hover.is_some() {
            self.hover_style = styles.hover;
        }
        if let Some(block) = styles.block {
            self.block = Some(block);
        }
        self.block = self.block.map(|v| v.style(self.style));
        self
    }
    #[inline]
    pub fn style(mut self, style: impl Into<Style>) -> Self {
        self.style = style.into();
        self
    }
    #[inline]
    pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
        self.focus_style = Some(style.into());
        self
    }
    #[inline]
    pub fn armed_style(mut self, style: impl Into<Style>) -> Self {
        self.armed_style = Some(style.into());
        self
    }
    pub fn armed_delay(mut self, delay: Duration) -> Self {
        self.armed_delay = Some(delay);
        self
    }
    pub fn hover_style(mut self, style: impl Into<Style>) -> Self {
        self.hover_style = Some(style.into());
        self
    }
    #[inline]
    pub fn text(mut self, text: impl Into<Text<'a>>) -> Self {
        self.text = text.into().centered();
        self
    }
    pub fn left_aligned(mut self) -> Self {
        self.text = self.text.left_aligned();
        self
    }
    pub fn right_aligned(mut self) -> Self {
        self.text = self.text.right_aligned();
        self
    }
    #[inline]
    pub fn block(mut self, block: Block<'a>) -> Self {
        self.block = Some(block);
        self.block = self.block.map(|v| v.style(self.style));
        self
    }
    pub fn width(&self) -> u16 {
        self.text.width() as u16 + block_size(&self.block).width
    }
    pub fn height(&self) -> u16 {
        self.text.height() as u16 + block_size(&self.block).height
    }
}
#[cfg(feature = "unstable-widget-ref")]
impl<'a> StatefulWidgetRef for Button<'a> {
    type State = ButtonState;
    fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        render_ref(self, area, buf, state);
    }
}
impl StatefulWidget for Button<'_> {
    type State = ButtonState;
    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        render_ref(&self, area, buf, state);
    }
}
fn render_ref(widget: &Button<'_>, area: Rect, buf: &mut Buffer, state: &mut ButtonState) {
    state.area = area;
    state.inner = widget.block.inner_if_some(area);
    state.armed_delay = widget.armed_delay;
    state.hover_enabled = widget.hover_style.is_some();
    let style = widget.style;
    let focus_style = if let Some(focus_style) = widget.focus_style {
        focus_style
    } else {
        revert_style(style)
    };
    let armed_style = if let Some(armed_style) = widget.armed_style {
        armed_style
    } else {
        if state.is_focused() {
            revert_style(focus_style)
        } else {
            revert_style(style)
        }
    };
    if widget.block.is_some() {
        widget.block.render(area, buf);
    } else {
        buf.set_style(area, style);
    }
    if state.mouse.hover.get() && widget.hover_style.is_some() {
        buf.set_style(state.inner, widget.hover_style.expect("style"))
    } else if state.is_focused() {
        buf.set_style(state.inner, focus_style);
    }
    if state.armed {
        let armed_area = Rect::new(
            state.inner.x + 1,
            state.inner.y,
            state.inner.width.saturating_sub(2),
            state.inner.height,
        );
        buf.set_style(armed_area, style.patch(armed_style));
    }
    let h = widget.text.height() as u16;
    let r = state.inner.height.saturating_sub(h) / 2;
    let area = Rect::new(state.inner.x, state.inner.y + r, state.inner.width, h);
    (&widget.text).render(area, buf);
}
impl Clone for ButtonState {
    fn clone(&self) -> Self {
        Self {
            area: self.area,
            inner: self.inner,
            hover_enabled: false,
            armed: self.armed,
            armed_delay: self.armed_delay,
            focus: FocusFlag::named(self.focus.name()),
            mouse: Default::default(),
            non_exhaustive: NonExhaustive,
        }
    }
}
impl Default for ButtonState {
    fn default() -> Self {
        Self {
            area: Default::default(),
            inner: Default::default(),
            hover_enabled: false,
            armed: false,
            armed_delay: None,
            focus: Default::default(),
            mouse: Default::default(),
            non_exhaustive: NonExhaustive,
        }
    }
}
impl ButtonState {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn named(name: &str) -> Self {
        Self {
            focus: FocusFlag::named(name),
            ..Default::default()
        }
    }
}
impl HasFocus for ButtonState {
    fn build(&self, builder: &mut FocusBuilder) {
        builder.leaf_widget(self);
    }
    #[inline]
    fn focus(&self) -> FocusFlag {
        self.focus.clone()
    }
    #[inline]
    fn area(&self) -> Rect {
        self.area
    }
}
impl RelocatableState for ButtonState {
    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
        self.area = relocate_area(self.area, shift, clip);
        self.inner = relocate_area(self.inner, shift, clip);
    }
}
pub(crate) mod event {
    use rat_event::{ConsumedEvent, Outcome};
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub enum ButtonOutcome {
        Continue,
        Unchanged,
        Changed,
        Pressed,
    }
    impl ConsumedEvent for ButtonOutcome {
        fn is_consumed(&self) -> bool {
            *self != ButtonOutcome::Continue
        }
    }
    impl From<ButtonOutcome> for Outcome {
        fn from(value: ButtonOutcome) -> Self {
            match value {
                ButtonOutcome::Continue => Outcome::Continue,
                ButtonOutcome::Unchanged => Outcome::Unchanged,
                ButtonOutcome::Changed => Outcome::Changed,
                ButtonOutcome::Pressed => Outcome::Changed,
            }
        }
    }
}
impl HandleEvent<crossterm::event::Event, Regular, ButtonOutcome> for ButtonState {
    fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> ButtonOutcome {
        let r = if self.is_focused() {
            if have_keyboard_enhancement() {
                match event {
                    ct_event!(keycode press Enter) | ct_event!(key press ' ') => {
                        self.armed = true;
                        ButtonOutcome::Changed
                    }
                    ct_event!(keycode release Enter) | ct_event!(key release ' ') => {
                        if self.armed {
                            if let Some(delay) = self.armed_delay {
                                thread::sleep(delay);
                            }
                            self.armed = false;
                            ButtonOutcome::Pressed
                        } else {
                            ButtonOutcome::Unchanged
                        }
                    }
                    _ => ButtonOutcome::Continue,
                }
            } else {
                match event {
                    ct_event!(keycode press Enter) | ct_event!(key press ' ') => {
                        ButtonOutcome::Pressed
                    }
                    _ => ButtonOutcome::Continue,
                }
            }
        } else {
            ButtonOutcome::Continue
        };
        if r == ButtonOutcome::Continue {
            HandleEvent::handle(self, event, MouseOnly)
        } else {
            r
        }
    }
}
impl HandleEvent<crossterm::event::Event, MouseOnly, ButtonOutcome> for ButtonState {
    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> ButtonOutcome {
        match event {
            ct_event!(mouse down Left for column, row) => {
                if self.area.contains((*column, *row).into()) {
                    self.armed = true;
                    ButtonOutcome::Changed
                } else {
                    ButtonOutcome::Continue
                }
            }
            ct_event!(mouse up Left for column, row) => {
                if self.area.contains((*column, *row).into()) {
                    if self.armed {
                        self.armed = false;
                        ButtonOutcome::Pressed
                    } else {
                        ButtonOutcome::Continue
                    }
                } else {
                    if self.armed {
                        self.armed = false;
                        ButtonOutcome::Changed
                    } else {
                        ButtonOutcome::Continue
                    }
                }
            }
            ct_event!(mouse any for m) if self.mouse.hover(self.area, m) => ButtonOutcome::Changed,
            _ => ButtonOutcome::Continue,
        }
    }
}
impl HandleEvent<crossterm::event::Event, crossterm::event::KeyEvent, ButtonOutcome>
    for ButtonState
{
    fn handle(
        &mut self,
        event: &crossterm::event::Event,
        hotkey: crossterm::event::KeyEvent,
    ) -> ButtonOutcome {
        use crossterm::event::Event;
        let r = match event {
            Event::Key(key) => {
                if have_keyboard_enhancement() {
                    if hotkey.code == key.code && hotkey.modifiers == key.modifiers {
                        if key.kind == crossterm::event::KeyEventKind::Press {
                            self.armed = true;
                            ButtonOutcome::Changed
                        } else if key.kind == crossterm::event::KeyEventKind::Release {
                            if self.armed {
                                if let Some(delay) = self.armed_delay {
                                    thread::sleep(delay);
                                }
                                self.armed = false;
                                ButtonOutcome::Pressed
                            } else {
                                ButtonOutcome::Unchanged
                            }
                        } else {
                            ButtonOutcome::Continue
                        }
                    } else {
                        ButtonOutcome::Continue
                    }
                } else {
                    if hotkey.code == key.code && hotkey.modifiers == key.modifiers {
                        if key.kind == crossterm::event::KeyEventKind::Press {
                            ButtonOutcome::Pressed
                        } else {
                            ButtonOutcome::Continue
                        }
                    } else {
                        ButtonOutcome::Continue
                    }
                }
            }
            _ => ButtonOutcome::Continue,
        };
        r.or_else(|| self.handle(event, Regular))
    }
}
pub fn handle_events(
    state: &mut ButtonState,
    focus: bool,
    event: &crossterm::event::Event,
) -> ButtonOutcome {
    state.focus.set(focus);
    HandleEvent::handle(state, event, Regular)
}
pub fn handle_mouse_events(
    state: &mut ButtonState,
    event: &crossterm::event::Event,
) -> ButtonOutcome {
    HandleEvent::handle(state, event, MouseOnly)
}