# Copyright 2026, Jiri Eischmann <jiri@eischmann.cz> and the meshy contributors
# SPDX-License-Identifier: GPL-3.0-or-later

"""XDG Background Portal integration for background mode and autostart."""

import logging
import os

from gi.repository import Gio, GLib

log = logging.getLogger(__name__)

PORTAL_BUS_NAME = "org.freedesktop.portal.Desktop"
PORTAL_OBJECT_PATH = "/org/freedesktop/portal/desktop"
PORTAL_BACKGROUND_IFACE = "org.freedesktop.portal.Background"
PORTAL_REQUEST_IFACE = "org.freedesktop.portal.Request"


class BackgroundPortal:
    def __init__(self):
        self._bus = None
        self._signal_id = None

    def _ensure_bus(self):
        if self._bus is None:
            try:
                self._bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
            except GLib.Error as e:
                log.warning("Cannot connect to session bus: %s", e.message)
                return False
        return True

    def request_background(self, reason, autostart, commandline, callback):
        """Request permission to run in background and optionally autostart.

        callback(bg_granted, autostart_granted) is called on the main thread.
        """
        if not self._ensure_bus():
            GLib.idle_add(lambda: callback(False, False) or False)
            return

        token = f"meshy_bg_{os.getpid()}"
        sender = self._bus.get_unique_name().replace(".", "_").lstrip(":")
        request_path = f"/org/freedesktop/portal/desktop/request/{sender}/{token}"

        self._cleanup_signal()
        self._signal_id = self._bus.signal_subscribe(
            PORTAL_BUS_NAME,
            PORTAL_REQUEST_IFACE,
            "Response",
            request_path,
            None,
            Gio.DBusSignalFlags.NO_MATCH_RULE,
            lambda *args: self._on_response(callback, *args),
        )

        options = {
            "handle_token": GLib.Variant("s", token),
            "reason": GLib.Variant("s", reason),
            "autostart": GLib.Variant("b", autostart),
        }
        if commandline:
            options["commandline"] = GLib.Variant("as", commandline)

        params = GLib.Variant("(sa{sv})", ("", options))

        self._bus.call(
            PORTAL_BUS_NAME,
            PORTAL_OBJECT_PATH,
            PORTAL_BACKGROUND_IFACE,
            "RequestBackground",
            params,
            None,
            Gio.DBusCallFlags.NONE,
            30000,
            None,
            self._on_call_done,
        )

    def _on_call_done(self, bus, result):
        try:
            bus.call_finish(result)
        except GLib.Error as e:
            log.warning(
                "Could not request background permission via portal: %s",
                e.message,
            )
            self._cleanup_signal()

    def _on_response(self, callback, bus, sender, path, iface, signal, params):
        self._cleanup_signal()
        response = params.get_child_value(0).get_uint32()
        if response != 0:
            log.info("Background permission denied (response=%d)", response)
            GLib.idle_add(lambda: callback(False, False) or False)
            return

        results = params.get_child_value(1)
        bg_val = results.lookup_value("background", GLib.VariantType.new("b"))
        autostart_val = results.lookup_value("autostart", GLib.VariantType.new("b"))
        bg_granted = bg_val.get_boolean() if bg_val else False
        autostart_granted = autostart_val.get_boolean() if autostart_val else False

        log.info(
            "Background portal: bg=%s autostart=%s",
            bg_granted,
            autostart_granted,
        )
        GLib.idle_add(lambda: callback(bg_granted, autostart_granted) or False)

    def set_status(self, message):
        if not self._ensure_bus():
            return
        try:
            options = {}
            if message:
                options["message"] = GLib.Variant("s", message)
            self._bus.call_sync(
                PORTAL_BUS_NAME,
                PORTAL_OBJECT_PATH,
                PORTAL_BACKGROUND_IFACE,
                "SetStatus",
                GLib.Variant("(a{sv})", (options,)),
                None,
                Gio.DBusCallFlags.NONE,
                5000,
                None,
            )
        except GLib.Error as e:
            log.warning("Could not set background status via portal: %s", e.message)

    def clear_status(self):
        self.set_status("")

    def _cleanup_signal(self):
        if self._signal_id and self._bus:
            self._bus.signal_unsubscribe(self._signal_id)
            self._signal_id = None
