use crate::app::App;
use crate::content_page::ContentPage;
use crate::global_actions::GlobalActions;
use crate::i18n::i18n;
use crate::settings::keybindings::Keybindings;
use gdk4::{Key, ModifierType};
use glib::{Object, Propagation, subclass};
use gtk4::{
    Accessible, Buildable, Button, CompositeTemplate, ConstraintTarget, Label, Native, Root, ShortcutManager, Stack,
    Widget, Window, prelude::*, subclass::prelude::*,
};
use libadwaita::{ShortcutLabel, Window as AdwWindow, subclass::prelude::*};
use std::cell::RefCell;

#[derive(Debug, Default, Clone)]
pub enum KeybindState {
    Enabled(String),
    #[default]
    Disabled,
    Canceled,
    Illegal,
}

mod imp {
    use super::*;

    #[derive(Debug, Default, CompositeTemplate)]
    #[template(file = "data/resources/ui_templates/settings/keybind_editor.blp")]
    pub struct KeybindingEditor {
        #[template_child]
        pub set_button: TemplateChild<Button>,
        #[template_child]
        pub shortcut_meta: TemplateChild<Label>,
        #[template_child]
        pub stack: TemplateChild<Stack>,
        #[template_child]
        pub instruction_label: TemplateChild<Label>,
        #[template_child]
        pub shortcut_label: TemplateChild<ShortcutLabel>,

        pub keybinding_internal: RefCell<KeybindState>,
        pub property_name: RefCell<String>,
        pub settings_obj: RefCell<Option<Object>>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for KeybindingEditor {
        const NAME: &'static str = "KeybindingEditor";
        type ParentType = AdwWindow;
        type Type = super::KeybindingEditor;

        fn class_init(klass: &mut Self::Class) {
            klass.bind_template();
            klass.bind_template_callbacks();
        }

        fn instance_init(obj: &subclass::InitializingObject<Self>) {
            obj.init_template();
        }
    }

    impl ObjectImpl for KeybindingEditor {}

    impl WidgetImpl for KeybindingEditor {}

    impl WindowImpl for KeybindingEditor {}

    impl AdwWindowImpl for KeybindingEditor {}

    #[gtk4::template_callbacks]
    impl KeybindingEditor {
        #[template_callback]
        fn on_key_pressed(&self, key: Key, _keycode: u32, state: ModifierType) -> Propagation {
            let modifier = Keybindings::clean_modifier(state);

            if key == Key::Escape {
                self.obj().close();
                return Propagation::Stop;
            }

            if key == Key::BackSpace {
                self.shortcut_meta.set_label(&i18n("Disable Keybinding"));
                self.set_button.set_visible(true);
                self.stack.set_visible_child_name("confirm");
                self.keybinding_internal.replace(KeybindState::Disabled);
                return Propagation::Proceed;
            }

            let internal_shortcut = gtk4::accelerator_name(key, modifier).to_string();

            if Keybindings::parse_keyval(key).is_some() {
                self.set_button.set_visible(true);
                self.shortcut_label.set_accelerator(&internal_shortcut);
                self.stack.set_visible_child_name("vis");
                self.keybinding_internal
                    .replace(KeybindState::Enabled(internal_shortcut));
            } else {
                self.set_button.set_visible(false);
                self.shortcut_meta.set_label(&i18n("Illegal Keybinding"));
                self.stack.set_visible_child_name("confirm");
                self.keybinding_internal.replace(KeybindState::Illegal);
            }

            Propagation::Proceed
        }

        #[template_callback]
        fn on_set_clicked(&self) {
            let keybinding = (*self.keybinding_internal.borrow()).clone();
            let Some(settings_obj) = self.settings_obj.borrow().clone() else {
                return;
            };

            let value = match keybinding {
                KeybindState::Canceled | KeybindState::Illegal => return,
                KeybindState::Disabled => None,
                KeybindState::Enabled(keybind) => Some(keybind),
            };

            settings_obj.set_property(self.property_name.borrow().as_str(), value);

            if App::default().settings().write().is_err() {
                ContentPage::instance().simple_message(&i18n("Failed to write keybinding"));
            }

            GlobalActions::set_accels();

            self.obj().close();
        }
    }
}

glib::wrapper! {
    pub struct KeybindingEditor(ObjectSubclass<imp::KeybindingEditor>)
        @extends Widget, Window, AdwWindow,
        @implements Accessible, Buildable, ConstraintTarget, Native, Root, ShortcutManager;
}

impl Default for KeybindingEditor {
    fn default() -> Self {
        glib::Object::new::<Self>()
    }
}

impl KeybindingEditor {
    pub fn new(setting_name: &str, settings_obj: Option<Object>, property_name: &str) -> Self {
        let obj = Self::default();
        let imp = obj.imp();
        imp.instruction_label.set_label(setting_name);
        imp.property_name.replace(property_name.to_string());
        imp.settings_obj.replace(settings_obj);
        obj
    }
}
