use crate::account_widget::AccountWidget;
use crate::app::App;
use crate::sidebar::SideBar;
use eyre::{Result, eyre};
use gdk4::Paintable;
use gio::Menu;
use glib::Bytes;
use glib::{Properties, subclass::prelude::*, subclass::*};
use gtk4::{
    Accessible, Box, Buildable, CompositeTemplate, ConstraintTarget, MenuButton, PopoverMenu, Widget, prelude::*,
    subclass::prelude::*,
};
use news_flash::NewsFlash;
use news_flash::models::{PluginCapabilities, PluginID, PluginIcon};
use std::cell::RefCell;

use super::ContentPage;

mod imp {
    use super::*;

    #[derive(Debug, Default, CompositeTemplate, Properties)]
    #[properties(wrapper_type = super::SidebarColumn)]
    #[template(file = "data/resources/ui_templates/sidebar/column.blp")]
    pub struct SidebarColumn {
        #[template_child]
        pub add_button: TemplateChild<MenuButton>,
        #[template_child]
        pub sidebar: TemplateChild<SideBar>,
        #[template_child]
        pub popover: TemplateChild<PopoverMenu>,
        #[template_child]
        pub account_menu: TemplateChild<Menu>,
        #[template_child]
        pub account_widget: TemplateChild<AccountWidget>,

        #[property(get, set, name = "account-paintable", nullable)]
        pub account_paintable: RefCell<Option<Paintable>>,
    }

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

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

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

    #[glib::derived_properties]
    impl ObjectImpl for SidebarColumn {
        fn constructed(&self) {
            self.popover.add_child(&*self.account_widget, "account_widget");

            App::default().connect_features_notify(Self::update_add_button_sensitive);
            App::default().connect_is_offline_notify(Self::update_add_button_sensitive);

            self.parent_constructed();
        }
    }

    impl WidgetImpl for SidebarColumn {}

    impl BoxImpl for SidebarColumn {}

    #[gtk4::template_callbacks]
    impl SidebarColumn {
        #[template_callback]
        pub fn is_paintable_some(&self, paintable: Option<Paintable>) -> bool {
            paintable.is_some()
        }

        #[template_callback]
        pub fn is_paintable_none(&self, paintable: Option<Paintable>) -> bool {
            paintable.is_none()
        }

        fn update_add_button_sensitive(app: &App) {
            let features = app.features();
            let is_offline = app.is_offline();
            let sensitive = !is_offline && features.as_ref().contains(PluginCapabilities::ADD_REMOVE_FEEDS);

            super::SidebarColumn::instance()
                .imp()
                .add_button
                .set_sensitive(sensitive);
        }
    }
}

glib::wrapper! {
    pub struct SidebarColumn(ObjectSubclass<imp::SidebarColumn>)
        @extends Widget, Box,
        @implements Accessible, Buildable, ConstraintTarget;
}

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

impl SidebarColumn {
    pub fn instance() -> Self {
        ContentPage::instance().imp().sidebar_column.get()
    }

    pub async fn set_account(&self, id: &PluginID, user_name: Option<&str>) -> Result<()> {
        let mut list = NewsFlash::list_backends();

        let Some(info) = list.remove(id) else {
            let msg = format!("Try loading branding failed. Backend '{id}' not found.");
            tracing::warn!(%msg);
            return Err(eyre!(msg));
        };

        let user = user_name.map(str::to_string).unwrap_or_else(|| info.name.clone());

        if let Some(plugin_icon) = info.icon_symbolic {
            match plugin_icon {
                PluginIcon::Vector(vector_icon) => {
                    let bytes = Bytes::from_owned(vector_icon.data);
                    let svg = gtk4::Svg::from_bytes(&bytes);
                    self.set_account_paintable(Some(svg));
                }
                PluginIcon::Pixel(_icon) => tracing::warn!("Pixel based icon not valid for account button"),
            }
        }

        if let Some(plugin_icon) = info.icon {
            match plugin_icon {
                PluginIcon::Vector(vector_icon) => {
                    self.imp().account_widget.set_account(Some(vector_icon), &user).await
                }
                PluginIcon::Pixel(_icon) => tracing::warn!("Pixel based icon not valid for account widget"),
            }
        }

        Ok(())
    }
}
