//! Main application window — the libadwaita shell that hosts the
//! three pages (Settings, Origins, Printers). Composition is
//! declared in `data/ui/window.blp`; this file is the Rust side
//! of the GObject template plus the binding logic.
//!
//! Templates: gtk-rs's `CompositeTemplate` macro reads the .ui
//! file embedded via GResource and wires up named children
//! (`#[template_child]`) to fields on our struct. The standard
//! GNOME-app pattern.

use crate::about::ZbplAboutPage;
use crate::dbus::AgentClient;
use crate::logs::ZbplLogsPage;
use crate::origins::ZbplOriginsPage;
use crate::printers::ZbplPrintersPage;
use crate::settings::ZbplSettingsPage;
use crate::test_prints::ZbplTestPrintsPage;
use adw::prelude::*;
use adw::subclass::prelude::*;
use gtk::{glib, CompositeTemplate};
use std::cell::OnceCell;
use std::rc::Rc;

mod imp {
    use super::*;

    #[derive(Debug, Default, CompositeTemplate)]
    #[template(resource = "/com/printplatformpro/BrowserAgent/ui/window.ui")]
    pub struct ZbplWindow {
        #[template_child]
        pub toast_overlay: TemplateChild<adw::ToastOverlay>,
        #[template_child]
        pub settings_page: TemplateChild<ZbplSettingsPage>,
        #[template_child]
        pub origins_page: TemplateChild<ZbplOriginsPage>,
        #[template_child]
        pub printers_page: TemplateChild<ZbplPrintersPage>,
        #[template_child]
        pub test_prints_page: TemplateChild<ZbplTestPrintsPage>,
        #[template_child]
        pub logs_page: TemplateChild<ZbplLogsPage>,
        #[template_child]
        pub about_page: TemplateChild<ZbplAboutPage>,

        pub agent: OnceCell<Rc<AgentClient>>,
    }

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

        fn class_init(klass: &mut Self::Class) {
            // Make sure the page subclasses are registered with
            // GObject before the template references them by name.
            ZbplSettingsPage::ensure_type();
            ZbplOriginsPage::ensure_type();
            ZbplPrintersPage::ensure_type();
            ZbplTestPrintsPage::ensure_type();
            ZbplLogsPage::ensure_type();
            ZbplAboutPage::ensure_type();
            klass.bind_template();
        }

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

    impl ObjectImpl for ZbplWindow {}
    impl WidgetImpl for ZbplWindow {}
    impl WindowImpl for ZbplWindow {}
    impl ApplicationWindowImpl for ZbplWindow {}
    impl AdwApplicationWindowImpl for ZbplWindow {}
}

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

impl ZbplWindow {
    pub fn new(app: &adw::Application, agent: Rc<AgentClient>) -> Self {
        let win: Self = glib::Object::builder().property("application", app).build();
        let imp = win.imp();
        imp.agent.set(agent.clone()).ok();
        // Push the agent client into each page so they can issue
        // their own D-Bus calls without bouncing through the
        // window. Each page is responsible for refresh + binding.
        imp.settings_page.bind_agent(agent.clone());
        imp.origins_page.bind_agent(agent.clone());
        imp.printers_page.bind_agent(agent.clone());
        imp.test_prints_page.bind_agent(agent.clone());
        imp.logs_page.bind_agent(agent.clone());
        imp.about_page.bind_agent(agent.clone());
        win
    }

    /// Re-pull every page's data from the agent. Triggered after a
    /// mutation (approve/revoke/autostart toggle) so the UI shows
    /// the agent's actual state, not our local prediction.
    pub fn refresh_all(&self) {
        let imp = self.imp();
        imp.settings_page.refresh();
        imp.origins_page.refresh();
        imp.printers_page.refresh();
        imp.test_prints_page.refresh();
        imp.logs_page.refresh();
        imp.about_page.refresh();
    }

    /// Display a toast in the main window's overlay. Pages call
    /// this via `parent_window().toast(...)` to surface a transient
    /// success / failure message instead of dumping to stderr.
    /// 4-second timeout is the libadwaita default and what feels
    /// right for both "Revoked" success and "Couldn't reach agent"
    /// errors — long enough to read, short enough to not linger.
    pub fn toast(&self, msg: &str) {
        let toast = adw::Toast::builder().title(msg).timeout(4).build();
        self.imp().toast_overlay.add_toast(toast);
    }
}
