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

import tests as xdp

import dbus
import pytest


FILECHOOSER_RESULTS = {
    "uris": ["file:///test.txt", "file:///example/test2.txt"],
    "choices": [("encoding", "utf8"), ("reencode", "true"), ("third", "a")],
}


@pytest.fixture
def required_templates():
    return {
        "filechooser": {
            "results": dbus.Dictionary(FILECHOOSER_RESULTS, signature="sv"),
        },
        "lockdown": {},
    }


class TestFilechooser:
    def test_version(self, portals, dbus_con):
        xdp.check_version(dbus_con, "FileChooser", 4)

    def test_open_file_basic(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        title = "Test"
        accept_label = "Accept"
        multiple = True
        options = {
            "accept_label": accept_label,
            "multiple": multiple,
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "OpenFile",
            parent_window="",
            title=title,
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("OpenFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[1] == app_id
        assert args[2] == ""  # parent window
        assert args[3] == title
        assert args[4]["accept_label"] == accept_label
        assert args[4]["multiple"] == multiple

    @pytest.mark.parametrize("template_params", ({"filechooser": {"response": 1}},))
    def test_open_file_cancel(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        title = "Test"
        accept_label = "Accept"
        multiple = True
        options = {
            "accept_label": accept_label,
            "multiple": multiple,
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "OpenFile",
            parent_window="",
            title=title,
            options=options,
        )

        assert response
        assert response.response == 1

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("OpenFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[1] == app_id
        assert args[2] == ""  # parent window
        assert args[3] == title
        assert args[4]["accept_label"] == accept_label
        assert args[4]["multiple"] == multiple

    @pytest.mark.parametrize(
        "template_params", ({"filechooser": {"expect-close": True}},)
    )
    def test_open_file_close(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        title = "Test"
        accept_label = "Accept"
        multiple = True
        options = {
            "accept_label": accept_label,
            "multiple": multiple,
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        request.schedule_close(1000)
        request.call(
            "OpenFile",
            parent_window="",
            title=title,
            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("OpenFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[1] == app_id
        assert args[2] == ""  # parent window
        assert args[3] == title
        assert args[4]["accept_label"] == accept_label
        assert args[4]["multiple"] == multiple

    def test_open_file_filter1(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        options = {
            "filters": [
                (
                    "Images",
                    [
                        (dbus.UInt32(0), "*ico"),
                        (dbus.UInt32(1), "image/png"),
                    ],
                ),
                (
                    "Text",
                    [
                        (dbus.UInt32(0), "*.txt"),
                    ],
                ),
            ],
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "OpenFile",
            parent_window="",
            title="Test",
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        method_calls = mock_intf.GetMethodCalls("OpenFile")
        assert len(method_calls) == 1

    def test_open_file_filter2(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")

        options = {
            "filters": [
                (
                    "Text",
                    [
                        # Invalid filter type
                        (dbus.UInt32(4), "*.txt"),
                    ],
                ),
            ],
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "OpenFile",
                parent_window="",
                title="Test",
                options=options,
            )
        assert (
            excinfo.value.get_dbus_name()
            == "org.freedesktop.portal.Error.InvalidArgument"
        )

    def test_open_file_current_filter1(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        options = {
            "filters": [
                (
                    "Images",
                    [
                        (dbus.UInt32(0), "*ico"),
                        (dbus.UInt32(1), "image/png"),
                    ],
                ),
                (
                    "Text",
                    [
                        (dbus.UInt32(0), "*.txt"),
                    ],
                ),
            ],
            "current_filter": (
                "Text",
                [
                    (dbus.UInt32(0), "*.txt"),
                ],
            ),
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "OpenFile",
            parent_window="",
            title="Test",
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        method_calls = mock_intf.GetMethodCalls("OpenFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[4]["current_filter"] == options["current_filter"]

    def test_open_file_current_filter2(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        options = {
            "current_filter": (
                "Text",
                [
                    (dbus.UInt32(0), "*.txt"),
                ],
            ),
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "OpenFile",
            parent_window="",
            title="Test",
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        method_calls = mock_intf.GetMethodCalls("OpenFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[4]["current_filter"] == options["current_filter"]

    def test_open_file_current_filter3(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")

        options = {
            "current_filter": (
                "Text",
                [
                    # Invalid filter type
                    (dbus.UInt32(6), "*.txt"),
                ],
            ),
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "OpenFile",
                parent_window="",
                title="Test",
                options=options,
            )
        assert (
            excinfo.value.get_dbus_name()
            == "org.freedesktop.portal.Error.InvalidArgument"
        )

    def test_open_file_current_filter4(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")

        options = {
            "filters": [
                (
                    "Images",
                    [
                        (dbus.UInt32(0), "*ico"),
                        (dbus.UInt32(1), "image/png"),
                    ],
                ),
                (
                    "Text",
                    [
                        (dbus.UInt32(0), "*.txt"),
                    ],
                ),
            ],
            "current_filter": (
                "Something else",
                [
                    (dbus.UInt32(0), "*.sth.else"),
                ],
            ),
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "OpenFile",
                parent_window="",
                title="Test",
                options=options,
            )
        assert (
            excinfo.value.get_dbus_name()
            == "org.freedesktop.portal.Error.InvalidArgument"
        )

    def test_open_file_choices1(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        options = {
            "choices": [
                (
                    "encoding",
                    "Encoding",
                    [
                        ("utf8", "Unicode"),
                        ("latin15", "Western"),
                    ],
                    "latin15",
                ),
                (
                    "reencode",
                    "Reencode",
                    [],
                    "false",
                ),
                (
                    "third",
                    "Third",
                    [("a", "A"), ("b", "B")],
                    "",
                ),
            ],
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "OpenFile",
            parent_window="",
            title="Test",
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        method_calls = mock_intf.GetMethodCalls("OpenFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[4]["choices"] == options["choices"]

    def test_open_file_choices_invalid(self, portals, dbus_con):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")

        invalid_choices = [
            (
                "encoding",
                "Encoding",
                [
                    ("utf8", ""),
                    ("latin15", "Western"),
                ],
                "latin15",
            ),
            (
                "encoding",
                "Encoding",
                [
                    ("", "Unicode"),
                    ("latin15", "Western"),
                ],
                "latin15",
            ),
            (
                "",
                "Encoding",
                [
                    ("utf8", "Unicode"),
                    ("latin15", "Western"),
                ],
                "latin15",
            ),
        ]

        for choice in invalid_choices:
            request = xdp.Request(dbus_con, filechooser_intf)
            with pytest.raises(dbus.exceptions.DBusException) as excinfo:
                options = {
                    "choices": [choice],
                }
                request.call(
                    "OpenFile",
                    parent_window="",
                    title="Test",
                    options=options,
                )
            assert (
                excinfo.value.get_dbus_name()
                == "org.freedesktop.portal.Error.InvalidArgument"
            )

    def test_save_file_basic(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        title = "Test"
        accept_label = "Accept"
        current_name = "File Name"
        options = {
            "accept_label": accept_label,
            "current_name": current_name,
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "SaveFile",
            parent_window="",
            title=title,
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("SaveFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[1] == app_id
        assert args[2] == ""  # parent window
        assert args[3] == title
        assert args[4]["accept_label"] == accept_label
        assert args[4]["current_name"] == current_name

    @pytest.mark.parametrize("template_params", ({"filechooser": {"response": 1}},))
    def test_save_file_cancel(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        title = "Test"
        accept_label = "Accept"
        current_name = "File Name"
        options = {
            "accept_label": accept_label,
            "current_name": current_name,
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "SaveFile",
            parent_window="",
            title=title,
            options=options,
        )

        assert response
        assert response.response == 1

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("SaveFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[1] == app_id
        assert args[2] == ""  # parent window
        assert args[3] == title
        assert args[4]["accept_label"] == accept_label
        assert args[4]["current_name"] == current_name

    @pytest.mark.parametrize(
        "template_params", ({"filechooser": {"expect-close": True}},)
    )
    def test_save_file_close(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        title = "Test"
        accept_label = "Accept"
        current_name = "File Name"
        options = {
            "accept_label": accept_label,
            "current_name": current_name,
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        request.schedule_close(1000)
        request.call(
            "SaveFile",
            parent_window="",
            title=title,
            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("SaveFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[1] == app_id
        assert args[2] == ""  # parent window
        assert args[3] == title
        assert args[4]["accept_label"] == accept_label
        assert args[4]["current_name"] == current_name

    def test_save_file_filters(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        options = {
            "filters": [
                (
                    "Images",
                    [
                        (dbus.UInt32(0), "*ico"),
                        (dbus.UInt32(1), "image/png"),
                    ],
                ),
                (
                    "Text",
                    [
                        (dbus.UInt32(0), "*.txt"),
                    ],
                ),
            ],
        }
        request = xdp.Request(dbus_con, filechooser_intf)
        response = request.call(
            "SaveFile",
            parent_window="",
            title="Title",
            options=options,
        )

        assert response
        assert response.response == 0
        assert response.results["uris"] == FILECHOOSER_RESULTS["uris"]

        # Check the impl portal was called with the right args
        method_calls = mock_intf.GetMethodCalls("SaveFile")
        assert len(method_calls) == 1
        _, args = method_calls.pop()
        assert args[4]["filters"] == options["filters"]

    @pytest.mark.parametrize(
        "template_params", ({"lockdown": {"disable-save-to-disk": True}},)
    )
    def test_save_file_lockdown(self, portals, dbus_con, app_id):
        filechooser_intf = xdp.get_portal_iface(dbus_con, "FileChooser")
        mock_intf = xdp.get_mock_iface(dbus_con)

        request = xdp.Request(dbus_con, filechooser_intf)
        with pytest.raises(dbus.exceptions.DBusException) as excinfo:
            request.call(
                "SaveFile",
                parent_window="",
                title="Title",
                options={},
            )
        assert (
            excinfo.value.get_dbus_name() == "org.freedesktop.portal.Error.NotAllowed"
        )

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