use adw::{prelude::*, subclass::prelude::*};
use gtk::{glib, glib::clone, CompositeTemplate};

use crate::{
    session::model::{Permissions, PowerLevel, POWER_LEVEL_ADMIN, POWER_LEVEL_MOD},
    utils::BoundObject,
};

mod imp {
    use std::cell::Cell;

    use glib::subclass::InitializingObject;

    use super::*;

    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
    #[template(resource = "/org/gnome/Fractal/ui/components/power_level_selection/popover.ui")]
    #[properties(wrapper_type = super::PowerLevelSelectionPopover)]
    pub struct PowerLevelSelectionPopover {
        #[template_child]
        pub admin_row: TemplateChild<gtk::ListBoxRow>,
        #[template_child]
        pub admin_selected: TemplateChild<gtk::Image>,
        #[template_child]
        pub mod_row: TemplateChild<gtk::ListBoxRow>,
        #[template_child]
        pub mod_selected: TemplateChild<gtk::Image>,
        #[template_child]
        pub default_row: TemplateChild<gtk::ListBoxRow>,
        #[template_child]
        pub default_pl_label: TemplateChild<gtk::Label>,
        #[template_child]
        pub default_selected: TemplateChild<gtk::Image>,
        #[template_child]
        pub muted_row: TemplateChild<gtk::ListBoxRow>,
        #[template_child]
        pub muted_pl_label: TemplateChild<gtk::Label>,
        #[template_child]
        pub muted_selected: TemplateChild<gtk::Image>,
        #[template_child]
        pub custom_row: TemplateChild<adw::SpinRow>,
        #[template_child]
        pub custom_adjustment: TemplateChild<gtk::Adjustment>,
        #[template_child]
        pub custom_confirm: TemplateChild<gtk::Button>,
        /// The permissions to watch.
        #[property(get, set = Self::set_permissions, explicit_notify, nullable)]
        pub permissions: BoundObject<Permissions>,
        /// The selected power level.
        #[property(get, set = Self::set_selected_power_level, explicit_notify)]
        pub selected_power_level: Cell<PowerLevel>,
    }

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

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

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

    #[glib::derived_properties]
    impl ObjectImpl for PowerLevelSelectionPopover {}

    impl WidgetImpl for PowerLevelSelectionPopover {}
    impl PopoverImpl for PowerLevelSelectionPopover {}

    impl PowerLevelSelectionPopover {
        /// Set the permissions to watch.
        fn set_permissions(&self, permissions: Option<Permissions>) {
            if self.permissions.obj() == permissions {
                return;
            }
            let obj = self.obj();

            self.permissions.disconnect_signals();

            if let Some(permissions) = permissions {
                let own_pl_handler = permissions.connect_own_power_level_notify(clone!(
                    #[weak(rename_to = imp)]
                    self,
                    move |_| {
                        imp.update();
                    }
                ));
                let default_pl_handler = permissions.connect_default_power_level_notify(clone!(
                    #[weak(rename_to = imp)]
                    self,
                    move |_| {
                        imp.update_default();
                        imp.update_muted();
                        imp.update_selection();
                    }
                ));
                let muted_pl_handler = permissions.connect_mute_power_level_notify(clone!(
                    #[weak(rename_to = imp)]
                    self,
                    move |_| {
                        imp.update_muted();
                        imp.update_selection();
                    }
                ));

                self.permissions.set(
                    permissions,
                    vec![own_pl_handler, default_pl_handler, muted_pl_handler],
                );
            }

            self.update();
            obj.notify_permissions();
        }

        /// Set the selected power level.
        fn set_selected_power_level(&self, power_level: PowerLevel) {
            if self.selected_power_level.get() == power_level {
                return;
            }

            self.selected_power_level.set(power_level);

            self.update_selection();
            self.update_custom();
            self.obj().notify_selected_power_level();
        }

        /// Update the rows.
        fn update(&self) {
            self.update_admin();
            self.update_mod();
            self.update_default();
            self.update_muted();
            self.update_custom();
            self.update_selection();
        }

        /// Update the admin row.
        fn update_admin(&self) {
            let Some(permissions) = self.permissions.obj() else {
                return;
            };

            let can_change_to_admin = permissions.own_power_level() >= POWER_LEVEL_ADMIN;

            if can_change_to_admin {
                self.admin_row.set_sensitive(true);
                self.admin_row.set_activatable(true);
            } else {
                self.admin_row.set_sensitive(false);
                self.admin_row.set_activatable(false);
            }
        }

        /// Update the moderator row.
        fn update_mod(&self) {
            let Some(permissions) = self.permissions.obj() else {
                return;
            };

            let can_change_to_mod = permissions.own_power_level() >= POWER_LEVEL_MOD;

            if can_change_to_mod {
                self.mod_row.set_sensitive(true);
                self.mod_row.set_activatable(true);
            } else {
                self.mod_row.set_sensitive(false);
                self.mod_row.set_activatable(false);
            }
        }

        /// Update the default row.
        fn update_default(&self) {
            let Some(permissions) = self.permissions.obj() else {
                return;
            };

            let default = permissions.default_power_level();
            self.default_pl_label.set_label(&default.to_string());

            let can_change_to_default = permissions.own_power_level() >= default;

            if can_change_to_default {
                self.default_row.set_sensitive(true);
                self.default_row.set_activatable(true);
            } else {
                self.default_row.set_sensitive(false);
                self.default_row.set_activatable(false);
            }
        }

        /// Update the muted row.
        fn update_muted(&self) {
            let Some(permissions) = self.permissions.obj() else {
                return;
            };

            let mute = permissions.mute_power_level();
            let default = permissions.default_power_level();

            if mute >= default {
                // There is no point in having the muted row since all users are muted by
                // default.
                self.muted_row.set_visible(false);
                return;
            }

            self.muted_pl_label.set_label(&mute.to_string());

            let can_change_to_muted = permissions.own_power_level() >= mute;

            if can_change_to_muted {
                self.muted_row.set_sensitive(true);
                self.muted_row.set_activatable(true);
            } else {
                self.muted_row.set_sensitive(false);
                self.muted_row.set_activatable(false);
            }

            self.muted_row.set_visible(true);
        }

        /// Update the custom row.
        fn update_custom(&self) {
            let Some(permissions) = self.permissions.obj() else {
                return;
            };

            self.custom_adjustment
                .set_upper(permissions.own_power_level() as f64);
            self.custom_adjustment
                .set_value(self.selected_power_level.get() as f64);
        }

        /// Update the selected row.
        fn update_selection(&self) {
            let Some(permissions) = self.permissions.obj() else {
                return;
            };

            let power_level = self.selected_power_level.get();

            self.admin_selected
                .set_opacity((power_level == POWER_LEVEL_ADMIN).into());
            self.mod_selected
                .set_opacity((power_level == POWER_LEVEL_MOD).into());
            self.default_selected
                .set_opacity((power_level == permissions.default_power_level()).into());
            self.muted_selected
                .set_opacity((power_level == permissions.mute_power_level()).into());
        }
    }
}

glib::wrapper! {
    /// An `AdwPreferencesRow` behaving like a combo box to select a room member's power level.
    pub struct PowerLevelSelectionPopover(ObjectSubclass<imp::PowerLevelSelectionPopover>)
        @extends gtk::Widget, gtk::Popover, @implements gtk::Accessible;
}

#[gtk::template_callbacks]
impl PowerLevelSelectionPopover {
    pub fn new() -> Self {
        glib::Object::new()
    }

    /// The custom value changed.
    #[template_callback]
    fn custom_value_changed(&self) {
        let imp = self.imp();
        let power_level = imp.custom_adjustment.value() as PowerLevel;
        let can_confirm = power_level != self.selected_power_level();

        imp.custom_confirm.set_sensitive(can_confirm);
    }

    /// The custom value was confirmed.
    #[template_callback]
    fn custom_value_confirmed(&self) {
        let imp = self.imp();
        let power_level = imp.custom_adjustment.value() as PowerLevel;

        self.popdown();
        self.set_selected_power_level(power_level);
    }

    /// A row was activated.
    #[template_callback]
    fn row_activated(&self, row: &gtk::ListBoxRow) {
        let Some(permissions) = self.permissions() else {
            return;
        };

        let power_level = match row.index() {
            0 => POWER_LEVEL_ADMIN,
            1 => POWER_LEVEL_MOD,
            2 => permissions.default_power_level(),
            3 => permissions.mute_power_level(),
            _ => return,
        };

        self.popdown();
        self.set_selected_power_level(power_level);
    }
}
