use dioxus::prelude::*;
use freya_elements::{
    self as dioxus_elements,
    events::{
        KeyboardEvent,
        MouseEvent,
    },
};
use freya_hooks::{
    use_animation_with_dependencies,
    use_applied_theme,
    use_focus,
    use_platform,
    AnimColor,
    AnimNum,
    Ease,
    Function,
    OnDepsChange,
    SwitchThemeWith,
};
use winit::window::CursorIcon;
#[derive(Props, Clone, PartialEq)]
pub struct SwitchProps {
    pub theme: Option<SwitchThemeWith>,
    pub enabled: bool,
    pub ontoggled: EventHandler<()>,
}
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum SwitchStatus {
    #[default]
    Idle,
    Hovering,
}
#[allow(non_snake_case)]
pub fn Switch(props: SwitchProps) -> Element {
    let theme = use_applied_theme!(&props.theme, switch);
    let animation = use_animation_with_dependencies(&theme, |conf, theme| {
        conf.on_deps_change(OnDepsChange::Finish);
        (
            AnimNum::new(2., 22.)
                .time(300)
                .function(Function::Expo)
                .ease(Ease::Out),
            AnimNum::new(14., 18.)
                .time(300)
                .function(Function::Expo)
                .ease(Ease::Out),
            AnimColor::new(&theme.background, &theme.enabled_background)
                .time(300)
                .function(Function::Expo)
                .ease(Ease::Out),
            AnimColor::new(&theme.thumb_background, &theme.enabled_thumb_background)
                .time(300)
                .function(Function::Expo)
                .ease(Ease::Out),
        )
    });
    let platform = use_platform();
    let mut status = use_signal(SwitchStatus::default);
    let mut focus = use_focus();
    let a11y_id = focus.attribute();
    use_drop(move || {
        if *status.read() == SwitchStatus::Hovering {
            platform.set_cursor(CursorIcon::default());
        }
    });
    let onmousedown = |e: MouseEvent| {
        e.stop_propagation();
    };
    let onmouseleave = move |e: MouseEvent| {
        e.stop_propagation();
        *status.write() = SwitchStatus::Idle;
        platform.set_cursor(CursorIcon::default());
    };
    let onmouseenter = move |e: MouseEvent| {
        e.stop_propagation();
        *status.write() = SwitchStatus::Hovering;
        platform.set_cursor(CursorIcon::Pointer);
    };
    let onclick = move |e: MouseEvent| {
        e.stop_propagation();
        focus.focus();
        props.ontoggled.call(());
    };
    let onglobalkeydown = move |e: KeyboardEvent| {
        if focus.validate_globalkeydown(&e) {
            props.ontoggled.call(());
        }
    };
    let (offset_x, size, background, circle) = &*animation.get().read_unchecked();
    let offset_x = offset_x.read();
    let size = size.read();
    let background = background.read();
    let circle = circle.read();
    let border = if focus.is_selected() {
        if props.enabled {
            format!("2 inner {}", theme.enabled_focus_border_fill)
        } else {
            format!("2 inner {}", theme.focus_border_fill)
        }
    } else {
        "none".to_string()
    };
    use_memo(use_reactive(&props.enabled, move |enabled| {
        if enabled {
            animation.start();
        } else if animation.peek_has_run_yet() {
            animation.reverse();
        }
    }));
    rsx!(
        rect {
            margin: "{theme.margin}",
            width: "48",
            height: "25",
            padding: "4",
            corner_radius: "50",
            background: "{background}",
            border: "{border}",
            onmousedown,
            onmouseenter,
            onmouseleave,
            onglobalkeydown,
            onclick,
            a11y_id,
            offset_x: "{offset_x}",
            main_align: "center",
            rect {
                background: "{circle}",
                width: "{size}",
                height: "{size}",
                corner_radius: "50",
            }
        }
    )
}
#[cfg(test)]
mod test {
    use dioxus::prelude::use_signal;
    use freya::prelude::*;
    use freya_testing::prelude::*;
    #[tokio::test]
    pub async fn switch() {
        fn switch_app() -> Element {
            let mut enabled = use_signal(|| false);
            rsx!(
                Switch {
                    enabled: *enabled.read(),
                    ontoggled: move |_| {
                        enabled.toggle();
                    }
                }
                label {
                    "{enabled}"
                }
            )
        }
        let mut utils = launch_test(switch_app);
        let root = utils.root();
        let label = root.get(1);
        utils.wait_for_update().await;
        assert_eq!(label.get(0).text(), Some("false"));
        utils.click_cursor((15., 15.)).await;
        assert_eq!(label.get(0).text(), Some("true"));
        utils.click_cursor((15., 15.)).await;
        assert_eq!(label.get(0).text(), Some("false"));
    }
}