use std::cell::OnceCell;

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

use crate::data::KrSecretService;
use crate::utils::error::*;
use crate::widgets::{AboutDialog, KrMainWindow};

mod imp {
    use super::*;

    #[derive(Debug, Default)]
    pub struct KrApp {
        pub window: OnceCell<KrMainWindow>,
        pub secret_service: KrSecretService,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for KrApp {
        const NAME: &'static str = "KrApp";
        type Type = super::KrApp;
        type ParentType = adw::Application;
    }

    impl ObjectImpl for KrApp {}

    impl ApplicationImpl for KrApp {
        fn activate(&self) {
            self.parent_activate();
            let obj = self.obj();

            if let Some(window) = self.window.get() {
                window.present();
                return;
            }

            let window = KrMainWindow::new();
            self.window.set(window.clone()).unwrap();
            obj.add_window(&window);

            let action_about = gio::SimpleAction::new("about", None);
            action_about.connect_activate(|_, _| AboutDialog::show());
            window.add_action(&action_about);

            let action = gio::SimpleAction::new("quit", None);
            action.connect_activate(clone!(@weak window => move |_, _| {
                window.close();
            }));
            obj.set_accels_for_action("app.quit", &["<ctrl>q"]);
            obj.add_action(&action);

            glib::MainContext::default().spawn_local(clone!(@weak obj => async move {
                let dbus_service = oo7::dbus::Service::new().await;
                dbus_service.handle_error("Failed to create secret service instance");

                if let Ok(dbus_service) = dbus_service {
                    obj.secret_service()
                        .set_dbus_service(dbus_service)
                        .await
                        .handle_error("Failed to receive collections");

                    if let Some(c) = obj.secret_service().login_collection() {
                        // Ensure the login collection is unlocked
                        c.unlock().await.handle_error("Unable to unlock default keyring");

                        obj.window().overview_page().set_login_collection(c);
                    }
                }
            }));

            window.present();
        }
    }

    impl GtkApplicationImpl for KrApp {}

    impl AdwApplicationImpl for KrApp {}
}

glib::wrapper! {
    pub struct KrApp(ObjectSubclass<imp::KrApp>)
        @extends gio::Application, gtk::Application, adw::Application,
        @implements gio::ActionMap, gio::ActionGroup;
}

impl KrApp {
    pub fn new() -> Self {
        glib::Object::builder()
            .property("application-id", crate::APP_ID)
            .build()
    }

    pub fn window(&self) -> KrMainWindow {
        self.imp().window.get().unwrap().clone()
    }

    pub fn secret_service(&self) -> KrSecretService {
        self.imp().secret_service.clone()
    }
}
