# 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 {"email": {}}


class TestEmail:
    def test_version(self, portals, dbus_con):
        """tests the version of the interface"""

        xdp.check_version(dbus_con, "Email", 4)

    def test_basic(self, portals, dbus_con):
        """test that the backend receives the expected data"""

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        addresses = ["mclasen@redhat.com"]
        subject = "Re: portal tests"
        body = "You have to see this"

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "addresses": addresses,
            "subject": subject,
            "body": body,
        }
        response = request.call(
            "ComposeEmail",
            parent_window="",
            options=options,
        )

        assert response
        assert response.response == 0

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[2] == ""  # parent window
        assert args[3]["addresses"] == addresses
        assert args[3]["subject"] == subject
        assert args[3]["body"] == body

    def test_address(self, portals, dbus_con):
        """test that an invalid address triggers an error"""

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        addresses = ["gibberish! not an email address\n%Q"]

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "addresses": addresses,
        }
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "ComposeEmail",
                parent_window="",
                options=options,
            )
        assert (
            excinfo.value.get_dbus_name()
            == "org.freedesktop.portal.Error.InvalidArgument"
        )

        # Check the impl portal was never called
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) == 0

    def test_punycode_address(self, portals, dbus_con):
        """test email address containing punycode"""

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        addresses = ["xn--franais-xxa@exemple.fr"]
        subject = "Re: portal tests"
        body = "To ASCII and beyond"

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "addresses": addresses,
            "subject": subject,
            "body": body,
        }
        response = request.call(
            "ComposeEmail",
            parent_window="",
            options=options,
        )

        assert response
        assert response.response == 0

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[2] == ""  # parent window
        assert args[3]["addresses"] == addresses
        assert args[3]["subject"] == subject
        assert args[3]["body"] == body

    def test_subject_multiline(self, portals, dbus_con):
        """test that an multiline subject triggers an error"""

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        subject = "not\na\nvalid\nsubject line"

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "subject": subject,
        }
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "ComposeEmail",
                parent_window="",
                options=options,
            )
        assert (
            excinfo.value.get_dbus_name()
            == "org.freedesktop.portal.Error.InvalidArgument"
        )

        # Check the impl portal was never called
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) == 0

    def test_subject_too_long(self, portals, dbus_con):
        """test that a subject line over 200 chars triggers an error"""

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        subject = "This subject line is too long" + "abc" * 60

        assert len(subject) > 200

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "subject": subject,
        }
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "ComposeEmail",
                parent_window="",
                options=options,
            )
        assert (
            excinfo.value.get_dbus_name()
            == "org.freedesktop.portal.Error.InvalidArgument"
        )

        # Check the impl portal was never called
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) == 0

    @pytest.mark.parametrize("template_params", ({"email": {"delay": 2000}},))
    def test_delay(self, portals, dbus_con):
        """
        Test that everything works as expected when the backend takes some
        time to send its response, as * is to be expected from a real backend
        that presents dialogs to the user.
        """

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        subject = "delay test"
        addresses = ["mclasen@redhat.com"]

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "addresses": addresses,
            "subject": subject,
        }

        start_time = time.perf_counter()

        response = request.call(
            "ComposeEmail",
            parent_window="",
            options=options,
        )

        assert response
        assert response.response == 0

        end_time = time.perf_counter()

        assert end_time - start_time > 2

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[2] == ""  # parent window
        assert args[3]["addresses"] == addresses
        assert args[3]["subject"] == subject

    @pytest.mark.parametrize("template_params", ({"email": {"response": 1}},))
    def test_cancel(self, portals, dbus_con):
        """
        Test that user cancellation works as expected.
        We simulate that the user cancels a hypothetical dialog,
        by telling the backend to return 1 as response code.
        And we check that we get the expected G_IO_ERROR_CANCELLED.
        """

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        subject = "cancel test"
        addresses = ["mclasen@redhat.com"]

        request = xdp.Request(dbus_con, email_intf)
        options = {
            "addresses": addresses,
            "subject": subject,
        }

        response = request.call(
            "ComposeEmail",
            parent_window="",
            options=options,
        )

        assert response
        assert response.response == 1

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[2] == ""  # parent window
        assert args[3]["addresses"] == addresses
        assert args[3]["subject"] == subject

    @pytest.mark.parametrize("template_params", ({"email": {"expect-close": True}},))
    def test_close(self, portals, dbus_con):
        """
        Test that app-side cancellation works as expected.
        We cancel the cancellable while while the hypothetical
        dialog is up, and tell the backend that it should
        expect a Close call. We rely on the backend to
        verify that that call actually happened.
        """

        email_intf = xdp.get_portal_iface(dbus_con, "Email")
        mock_intf = xdp.get_mock_iface(dbus_con)

        subject = "close test"
        addresses = ["mclasen@redhat.com"]

        request = xdp.Request(dbus_con, email_intf)
        request.schedule_close(1000)
        options = {
            "addresses": addresses,
            "subject": subject,
        }

        request.call(
            "ComposeEmail",
            parent_window="",
            options=options,
        )

        # Only true if the impl.Request was closed too
        assert request.closed

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("ComposeEmail")
        assert len(method_calls) > 0
        _, args = method_calls[-1]
        assert args[2] == ""  # parent window
        assert args[3]["addresses"] == addresses
        assert args[3]["subject"] == subject
