use crate::_private::NonExhaustive;
use crate::checkbox::event::CheckOutcome;
use crate::util::{block_size, revert_style};
use rat_event::util::MouseFlags;
use rat_event::{ct_event, 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, StatefulWidget, Text, Widget};
use ratatui::style::Style;
use ratatui::text::Span;
use ratatui::widgets::Block;
#[cfg(feature = "unstable-widget-ref")]
use ratatui::widgets::StatefulWidgetRef;
use std::cmp::max;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Debug, Clone)]
pub struct Checkbox<'a> {
    text: Text<'a>,
    checked: Option<bool>,
    default: Option<bool>,
    true_str: Span<'a>,
    false_str: Span<'a>,
    style: Style,
    focus_style: Option<Style>,
    block: Option<Block<'a>>,
}
#[derive(Debug, Clone)]
pub struct CheckboxStyle {
    pub style: Style,
    pub focus: Option<Style>,
    pub block: Option<Block<'static>>,
    pub true_str: Option<Span<'static>>,
    pub false_str: Option<Span<'static>>,
    pub non_exhaustive: NonExhaustive,
}
#[derive(Debug)]
pub struct CheckboxState {
    pub area: Rect,
    pub inner: Rect,
    pub check_area: Rect,
    pub text_area: Rect,
    pub checked: bool,
    pub default: bool,
    pub focus: FocusFlag,
    pub mouse: MouseFlags,
    pub non_exhaustive: NonExhaustive,
}
pub(crate) mod event {
    use rat_event::{ConsumedEvent, Outcome};
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub enum CheckOutcome {
        Continue,
        Unchanged,
        Changed,
        Value,
    }
    impl ConsumedEvent for CheckOutcome {
        fn is_consumed(&self) -> bool {
            *self != CheckOutcome::Continue
        }
    }
    impl From<CheckOutcome> for Outcome {
        fn from(value: CheckOutcome) -> Self {
            match value {
                CheckOutcome::Continue => Outcome::Continue,
                CheckOutcome::Unchanged => Outcome::Unchanged,
                CheckOutcome::Changed => Outcome::Changed,
                CheckOutcome::Value => Outcome::Changed,
            }
        }
    }
}
impl Default for CheckboxStyle {
    fn default() -> Self {
        Self {
            style: Default::default(),
            focus: None,
            block: Default::default(),
            true_str: None,
            false_str: None,
            non_exhaustive: NonExhaustive,
        }
    }
}
impl Default for Checkbox<'_> {
    fn default() -> Self {
        Self {
            text: Default::default(),
            checked: None,
            default: None,
            true_str: Span::from("[\u{2713}]"),
            false_str: Span::from("[ ]"),
            style: Default::default(),
            focus_style: None,
            block: None,
        }
    }
}
impl<'a> Checkbox<'a> {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn styles(mut self, styles: CheckboxStyle) -> Self {
        self.style = styles.style;
        if styles.focus.is_some() {
            self.focus_style = styles.focus;
        }
        if let Some(block) = styles.block {
            self.block = Some(block);
        }
        if let Some(true_str) = styles.true_str {
            self.true_str = true_str;
        }
        if let Some(false_str) = styles.false_str {
            self.false_str = false_str;
        }
        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 text(mut self, text: impl Into<Text<'a>>) -> Self {
        self.text = text.into();
        self
    }
    pub fn checked(mut self, checked: bool) -> Self {
        self.checked = Some(checked);
        self
    }
    pub fn default_(mut self, default: bool) -> Self {
        self.default = Some(default);
        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 true_str(mut self, str: Span<'a>) -> Self {
        self.true_str = str;
        self
    }
    pub fn false_str(mut self, str: Span<'a>) -> Self {
        self.false_str = str;
        self
    }
    fn check_len(&self) -> u16 {
        max(
            self.true_str.content.graphemes(true).count(),
            self.false_str.content.graphemes(true).count(),
        ) as u16
    }
    pub fn width(&self) -> u16 {
        let chk_len = self.check_len();
        let txt_len = self.text.width() as u16;
        chk_len + 1 + txt_len + 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 Checkbox<'a> {
    type State = CheckboxState;
    fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        render_ref(self, area, buf, state);
    }
}
impl StatefulWidget for Checkbox<'_> {
    type State = CheckboxState;
    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
        render_ref(&self, area, buf, state);
    }
}
fn render_ref(widget: &Checkbox<'_>, area: Rect, buf: &mut Buffer, state: &mut CheckboxState) {
    state.area = area;
    state.inner = widget.block.inner_if_some(area);
    let chk_len = widget.check_len();
    state.check_area = Rect::new(state.inner.x, state.inner.y, chk_len, 1);
    state.text_area = Rect::new(
        state.inner.x + chk_len + 1,
        state.inner.y,
        state.inner.width.saturating_sub(chk_len + 1),
        state.inner.height,
    );
    if let Some(checked) = widget.checked {
        state.checked = checked;
    }
    if let Some(default) = widget.default {
        state.default = default;
    }
    let style = widget.style;
    let focus_style = if let Some(focus_style) = widget.focus_style {
        style.patch(focus_style)
    } else {
        revert_style(style)
    };
    if widget.block.is_some() {
        widget.block.render(area, buf);
        if state.focus.get() {
            buf.set_style(state.inner, focus_style);
        }
    } else {
        if state.focus.get() {
            buf.set_style(state.inner, focus_style);
        } else {
            buf.set_style(state.inner, widget.style);
        }
    }
    let cc = if state.checked {
        &widget.true_str
    } else {
        &widget.false_str
    };
    cc.render(state.check_area, buf);
    (&widget.text).render(state.text_area, buf);
}
impl Clone for CheckboxState {
    fn clone(&self) -> Self {
        Self {
            area: self.area,
            inner: self.inner,
            check_area: self.check_area,
            text_area: self.text_area,
            checked: self.checked,
            default: self.default,
            focus: FocusFlag::named(self.focus.name()),
            mouse: Default::default(),
            non_exhaustive: NonExhaustive,
        }
    }
}
impl Default for CheckboxState {
    fn default() -> Self {
        Self {
            area: Default::default(),
            inner: Default::default(),
            check_area: Default::default(),
            text_area: Default::default(),
            checked: false,
            default: false,
            focus: Default::default(),
            mouse: Default::default(),
            non_exhaustive: NonExhaustive,
        }
    }
}
impl HasFocus for CheckboxState {
    fn build(&self, builder: &mut FocusBuilder) {
        builder.leaf_widget(self);
    }
    fn focus(&self) -> FocusFlag {
        self.focus.clone()
    }
    fn area(&self) -> Rect {
        self.area
    }
}
impl RelocatableState for CheckboxState {
    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);
    }
}
impl CheckboxState {
    pub fn new() -> Self {
        Self::default()
    }
    pub fn named(name: &str) -> Self {
        Self {
            focus: FocusFlag::named(name),
            ..Default::default()
        }
    }
    pub fn checked(&self) -> bool {
        self.checked
    }
    pub fn set_checked(&mut self, checked: bool) -> bool {
        let old_value = self.checked;
        self.checked = checked;
        old_value != self.checked
    }
    pub fn default_(&self) -> bool {
        self.default
    }
    pub fn set_default(&mut self, default: bool) -> bool {
        let old_value = self.default;
        self.default = default;
        old_value != self.default
    }
    pub fn value(&self) -> bool {
        self.checked
    }
    pub fn set_value(&mut self, checked: bool) -> bool {
        let old_value = self.checked;
        self.checked = checked;
        old_value != self.checked
    }
    pub fn flip_checked(&mut self) {
        self.checked = !self.checked;
    }
}
impl HandleEvent<crossterm::event::Event, Regular, CheckOutcome> for CheckboxState {
    fn handle(&mut self, event: &crossterm::event::Event, _qualifier: Regular) -> CheckOutcome {
        let r = if self.is_focused() {
            match event {
                ct_event!(keycode press Enter) | ct_event!(key press ' ') => {
                    self.flip_checked();
                    CheckOutcome::Value
                }
                ct_event!(keycode press Backspace) | ct_event!(keycode press Delete) => {
                    self.set_value(self.default);
                    CheckOutcome::Value
                }
                _ => CheckOutcome::Continue,
            }
        } else {
            CheckOutcome::Continue
        };
        if r == CheckOutcome::Continue {
            HandleEvent::handle(self, event, MouseOnly)
        } else {
            r
        }
    }
}
impl HandleEvent<crossterm::event::Event, MouseOnly, CheckOutcome> for CheckboxState {
    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> CheckOutcome {
        match event {
            ct_event!(mouse any for m) if self.mouse.doubleclick(self.area, m) => {
                self.flip_checked();
                CheckOutcome::Value
            }
            _ => CheckOutcome::Continue,
        }
    }
}
pub fn handle_events(
    state: &mut CheckboxState,
    focus: bool,
    event: &crossterm::event::Event,
) -> CheckOutcome {
    state.focus.set(focus);
    HandleEvent::handle(state, event, Regular)
}
pub fn handle_mouse_events(
    state: &mut CheckboxState,
    event: &crossterm::event::Event,
) -> CheckOutcome {
    HandleEvent::handle(state, event, MouseOnly)
}