# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is formatted with Python Black

import tests as xdp

import dbus
import pytest
import time


@pytest.fixture
def required_templates():
    return {"globalshortcuts": {}}


class TestGlobalShortcuts:
    def test_version(self, portals, dbus_con):
        xdp.check_version(dbus_con, "GlobalShortcuts", 1)

    def test_create_close_session(self, portals, dbus_con, app_id):
        globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
        mock_intf = xdp.get_mock_iface(dbus_con)

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        options = {
            "session_handle_token": "session_token0",
        }
        response = request.call(
            "CreateSession",
            options=options,
        )

        assert response
        assert response.response == 0

        session = xdp.Session.from_response(dbus_con, response)
        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("CreateSession")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[1] == session.handle
        assert args[2] == app_id

        session.close()
        xdp.wait_for(lambda: session.closed)

    @pytest.mark.parametrize(
        "template_params", ({"globalshortcuts": {"force-close": 500}},)
    )
    def test_create_session_signal_closed(self, portals, dbus_con, app_id):
        globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
        mock_intf = xdp.get_mock_iface(dbus_con)

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        options = {
            "session_handle_token": "session_token0",
        }
        response = request.call(
            "CreateSession",
            options=options,
        )

        assert response
        assert response.response == 0

        session = xdp.Session.from_response(dbus_con, response)
        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("CreateSession")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[1] == session.handle
        assert args[2] == app_id

        # Now expect the backend to close it
        xdp.wait_for(lambda: session.closed)

    def test_bind_list_shortcuts(self, portals, dbus_con):
        globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        options = {
            "session_handle_token": "session_token0",
        }
        response = request.call(
            "CreateSession",
            options=options,
        )

        assert response
        assert response.response == 0

        session = xdp.Session.from_response(dbus_con, response)

        shortcuts = [
            (
                "binding1",
                {
                    "description": dbus.String("Binding #1", variant_level=1),
                    "preferred-trigger": dbus.String("CTRL+a", variant_level=1),
                },
            ),
            (
                "binding2",
                {
                    "description": dbus.String("Binding #2", variant_level=1),
                    "preferred-trigger": dbus.String("CTRL+b", variant_level=1),
                },
            ),
        ]

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        response = request.call(
            "BindShortcuts",
            session_handle=session.handle,
            shortcuts=shortcuts,
            parent_window="",
            options={},
        )

        assert response
        assert response.response == 0

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        options = {}
        response = request.call(
            "ListShortcuts",
            session_handle=session.handle,
            options=options,
        )

        assert response
        assert response.response == 0

        assert len(list(response.results["shortcuts"])) == len(list(shortcuts))

        session.close()
        xdp.wait_for(lambda: session.closed)

    def test_trigger(self, portals, dbus_con):
        globalshortcuts_intf = xdp.get_portal_iface(dbus_con, "GlobalShortcuts")
        mock_intf = xdp.get_mock_iface(dbus_con)

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        options = {
            "session_handle_token": "session_token0",
        }
        response = request.call(
            "CreateSession",
            options=options,
        )

        assert response
        assert response.response == 0

        session = xdp.Session.from_response(dbus_con, response)

        shortcuts = [
            (
                "binding1",
                {
                    "description": dbus.String("Binding #1", variant_level=1),
                    "preferred-trigger": dbus.String("CTRL+a", variant_level=1),
                },
            ),
        ]

        request = xdp.Request(dbus_con, globalshortcuts_intf)
        response = request.call(
            "BindShortcuts",
            session_handle=session.handle,
            shortcuts=shortcuts,
            parent_window="",
            options={},
        )

        assert response
        assert response.response == 0

        activated_count = 0
        deactivated_count = 0

        def cb_activated(session_handle, shortcut_id, timestamp, options):
            nonlocal activated_count
            now_since_epoch = int(time.time() * 1000000)
            # This assert will race twice a year on systems configured with
            # summer time timezone changes
            assert (
                now_since_epoch > timestamp
                and (now_since_epoch - 10 * 10001000) < timestamp
            )
            assert shortcut_id == "binding1"
            activated_count += 1

        def cb_deactivated(session_handle, shortcut_id, timestamp, options):
            nonlocal deactivated_count
            now_since_epoch = int(time.time() * 1000000)
            # This assert will race twice a year on systems configured with
            # summer time timezone changes
            assert (
                now_since_epoch > timestamp
                and (now_since_epoch - 10 * 10001000) < timestamp
            )
            assert shortcut_id == "binding1"
            deactivated_count += 1

        globalshortcuts_intf.connect_to_signal("Activated", cb_activated)
        globalshortcuts_intf.connect_to_signal("Deactivated", cb_deactivated)

        mock_intf.Trigger(session.handle, "binding1")

        xdp.wait_for(lambda: activated_count == 1 and deactivated_count == 1)
        assert not session.closed

        session.close()
        xdp.wait_for(lambda: session.closed)
