# 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 = 'meshy_bg_%d' % (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
