# SPDX-License-Identifier: LGPL-3.0-only
#
# This file is formatted with Python Black

from . import PortalTest

import gi
import logging

gi.require_version("Xdp", "1.0")
from gi.repository import GLib, Gio, Xdp

logger = logging.getLogger(__name__)

SVG_IMAGE_DATA = ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"16px\" width=\"16px\"/>")


class TestNotification(PortalTest):
    def test_version(self):
        self.assert_version_eq(2)

    def add_notification(
            self, version: int, id_to_set: str, notification_to_set: GLib.Variant
    ):
        params = {"version": version}
        self.setup_daemon(params)

        xdp = Xdp.Portal.new()
        assert xdp is not None

        notification_added = False

        def add_notification_done(portal, task, data):
            nonlocal notification_added
            notification_added = portal.add_notification_finish(task)
            self.mainloop.quit()

        xdp.add_notification(
            id=id_to_set,
            notification=notification_to_set,
            flags=Xdp.NotificationFlags.NONE,
            cancellable=None,
            callback=add_notification_done,
            data=None,
        )

        self.mainloop.run()

        method_calls = self.mock_interface.GetMethodCalls("AddNotification")
        assert len(method_calls) == 1
        timestamp, args = method_calls.pop(0)
        id, notification = args
        assert id == id_to_set
        assert notification_added

        return notification

    def test_add_notification(self):
        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        notification_to_set_dict.pop('invalid-property', None)
        assert dict(notification) == notification_to_set_dict

    def test_add_notification_bytes_icon(self):
        bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode('utf-8'))
        icon = Gio.BytesIcon.new(bytes);

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'icon': icon.serialize(),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        icon_to_set = notification_to_set_dict.pop('icon', None)
        icon = notification.pop('icon', None)
        assert dict(notification) == notification_to_set_dict
        (icon_type, handle) = icon
        assert icon_type == 'file-descriptor'
        input_stream = Gio.UnixInputStream.new(handle.take(), True)

        res_bytes = input_stream.read_bytes(bytes.get_size(), None)
        assert res_bytes.equal (bytes)

    def test_add_notification_file_icon(self):
        bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode('utf-8'))

        (file, iostream) = Gio.File.new_tmp("iconXXXXXX")
        stream = iostream.get_output_stream()
        stream.write_bytes(bytes)
        stream.close()
        icon = Gio.FileIcon.new (file)

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'icon': icon.serialize(),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                          )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        icon_to_set = notification_to_set_dict.pop('icon', None)
        icon = notification.pop('icon', None)
        assert dict(notification) == notification_to_set_dict
        (icon_type, handle) = icon
        assert icon_type == 'file-descriptor'
        input_stream = Gio.UnixInputStream.new(handle.take(), True)

        res_bytes = input_stream.read_bytes(bytes.get_size(), None)
        assert res_bytes.equal (bytes)

        file.delete()

    def test_add_notification_themed_icon(self):
        icon = Gio.ThemedIcon.new('someicon')

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'icon': icon.serialize(),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        icon_to_set = notification_to_set_dict.pop('icon', None)
        icon_out = notification.pop('icon', None)
        assert dict(notification) == notification_to_set_dict
        (icon_type, names) = icon_out
        assert icon_type == 'themed'

        assert names == icon.get_names()

    def test_add_notification_bytes_sound(self):
        bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode('utf-8'))
        icon = Gio.BytesIcon.new(bytes);

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'sound': GLib.Variant('(sv)', ('bytes', GLib.Variant('ay', bytes.get_data()))),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        sound_to_set = notification_to_set_dict.pop('sound', None)
        sound = notification.pop('sound', None)
        assert dict(notification) == notification_to_set_dict
        (sound_type, handle) = sound
        assert sound_type == 'file-descriptor'
        input_stream = Gio.UnixInputStream.new(handle.take(), True)

        res_bytes = input_stream.read_bytes(bytes.get_size(), None)
        assert res_bytes.equal (bytes)

    def test_add_notification_file_sound(self):
        bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode('utf-8'))

        (file, iostream) = Gio.File.new_tmp("soundXXXXXX")
        stream = iostream.get_output_stream()
        stream.write_bytes(bytes)
        stream.close()

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'sound': GLib.Variant('(sv)', ['file', GLib.Variant('s', file.get_uri())]),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                         )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        sound_to_set = notification_to_set_dict.pop('sound', None)
        sound_out = notification.pop('sound', None)
        assert dict(notification) == notification_to_set_dict
        (sound_type, handle) = sound_out
        assert sound_type == 'file-descriptor'
        input_stream = Gio.UnixInputStream.new(handle.take(), True)

        res_bytes = input_stream.read_bytes(bytes.get_size(), None)
        assert res_bytes.equal (bytes)

        file.delete()

    def test_add_notification_v1(self):
        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'invalid-property': GLib.Variant('s', 'invalid'),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )

        notification = self.add_notification(1, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        notification_to_set_dict.pop('invalid-property', None)
        assert dict(notification) == notification_to_set_dict

    def test_add_notification_bytes_icon_v1(self):
        bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode('utf-8'))
        icon = Gio.BytesIcon.new(bytes);

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'icon': icon.serialize(),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(1, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        icon_to_set = notification_to_set_dict.pop('icon', None)
        icon = notification.pop('icon', None)
        assert dict(notification) == notification_to_set_dict
        (icon_type, res_bytes) = icon
        assert icon_type == 'bytes'
        assert GLib.Bytes(res_bytes).equal (bytes)

    def test_add_notification_file_icon_v1(self):
        bytes = GLib.Bytes.new(SVG_IMAGE_DATA.encode('utf-8'))

        (file, iostream) = Gio.File.new_tmp("iconXXXXXX")
        stream = iostream.get_output_stream()
        stream.write_bytes(bytes)
        stream.close()
        icon = Gio.FileIcon.new (file)

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'icon': icon.serialize(),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(1, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        icon_to_set = notification_to_set_dict.pop('icon', None)
        icon = notification.pop('icon', None)
        assert dict(notification) == notification_to_set_dict
        (icon_type, res_bytes) = icon
        assert icon_type == 'bytes'
        assert GLib.Bytes(res_bytes).equal (bytes)

        file.delete()

    def test_add_notification_themed_icon_v1(self):
        icon = Gio.ThemedIcon.new('someicon')

        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'icon': icon.serialize(),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(2, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        icon_to_set = notification_to_set_dict.pop('icon', None)
        icon_out = notification.pop('icon', None)
        assert dict(notification) == notification_to_set_dict
        (icon_type, names) = icon_out
        assert icon_type == 'themed'

        assert names == icon.get_names()

    def test_add_notification_buttons(self):
        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'buttons': GLib.Variant('aa{sv}', [
                                                {
                                                    'label': GLib.Variant('s', 'button1'),
                                                    'action': GLib.Variant('s', 'action1'),
                                                    'purpose': GLib.Variant('s', 'some-purpose')
                                                    },
                                                {
                                                    'label': GLib.Variant('s', 'button2'),
                                                    'action': GLib.Variant('s', 'action2'),
                                                    'purpose': GLib.Variant('s', 'some-purpose')
                                                    },
                                                ]),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )
        notification = self.add_notification(2, 'someid', notification_to_set)

        assert dict(notification) == dict(notification_to_set)

    def test_add_notification_buttons_v1(self):
        notification_to_set = GLib.Variant('a{sv}',
                                           {'title': GLib.Variant('s', 'title'),
                                            'body': GLib.Variant('s', 'test notification body'),
                                            'priority': GLib.Variant('s', 'normal'),
                                            'buttons': GLib.Variant('aa{sv}', [
                                                {
                                                    'label': GLib.Variant('s', 'button1'),
                                                    'action': GLib.Variant('s', 'action1'),
                                                    'purpose': GLib.Variant('s', 'some-purpose')
                                                    },
                                                {
                                                    'label': GLib.Variant('s', 'button2'),
                                                    'action': GLib.Variant('s', 'action2'),
                                                    'purpose': GLib.Variant('s', 'some-purpose')
                                                    },
                                                ]),
                                            'default-action': GLib.Variant('s', 'test-action')}
                                           )

        notification = self.add_notification(1, 'someid', notification_to_set)

        notification_to_set_dict = dict(notification_to_set)
        buttons = notification_to_set_dict.pop('buttons', None)

        notification_to_set_dict['buttons'] = []
        for button in buttons:
            button.pop('purpose', None)
            notification_to_set_dict['buttons'].append(button)

        assert dict(notification) == notification_to_set_dict
