import pytest

from flask_restx import marshal, marshal_with, marshal_with_field, fields, Api, Resource

from collections import OrderedDict


# Add a dummy Resource to verify that the app is properly set.
class HelloWorld(Resource):
    def get(self):
        return {}


class MarshallingTest(object):
    def test_marshal(self):
        model = OrderedDict([("foo", fields.Raw)])
        marshal_dict = OrderedDict([("foo", "bar"), ("bat", "baz")])
        output = marshal(marshal_dict, model)
        assert isinstance(output, dict)
        assert not isinstance(output, OrderedDict)
        assert output == {"foo": "bar"}

    def test_marshal_wildcard_nested(self):
        nest = fields.Nested(
            OrderedDict([("thumbnail", fields.String), ("video", fields.String)])
        )
        wild = fields.Wildcard(nest)
        wildcard_fields = OrderedDict([("*", wild)])
        model = OrderedDict([("preview", fields.Nested(wildcard_fields))])
        sub_dict = OrderedDict(
            [
                ("9:16", {"thumbnail": 24, "video": 12}),
                ("16:9", {"thumbnail": 25, "video": 11}),
                ("1:1", {"thumbnail": 26, "video": 10}),
            ]
        )
        marshal_dict = OrderedDict([("preview", sub_dict)])
        output = marshal(marshal_dict, model)
        assert output == {
            "preview": {
                "1:1": {"thumbnail": "26", "video": "10"},
                "16:9": {"thumbnail": "25", "video": "11"},
                "9:16": {"thumbnail": "24", "video": "12"},
            }
        }

    def test_marshal_wildcard_list(self):
        wild = fields.Wildcard(fields.List(fields.String))
        wildcard_fields = OrderedDict([("*", wild)])
        model = OrderedDict([("preview", fields.Nested(wildcard_fields))])
        sub_dict = OrderedDict(
            [("1:1", [1, 2, 3]), ("16:9", [4, 5, 6]), ("9:16", [7, 8, 9])]
        )
        marshal_dict = OrderedDict([("preview", sub_dict)])
        output = marshal(marshal_dict, model)
        assert output == {
            "preview": {
                "9:16": ["7", "8", "9"],
                "16:9": ["4", "5", "6"],
                "1:1": ["1", "2", "3"],
            }
        }

    def test_marshal_with_envelope(self):
        model = OrderedDict([("foo", fields.Raw)])
        marshal_dict = OrderedDict([("foo", "bar"), ("bat", "baz")])
        output = marshal(marshal_dict, model, envelope="hey")
        assert output == {"hey": {"foo": "bar"}}

    def test_marshal_wildcard_with_envelope(self):
        wild = fields.Wildcard(fields.String)
        model = OrderedDict([("foo", fields.Raw), ("*", wild)])
        marshal_dict = OrderedDict(
            [("foo", {"bat": "baz"}), ("a", "toto"), ("b", "tata")]
        )
        output = marshal(marshal_dict, model, envelope="hey")
        assert output == {"hey": {"a": "toto", "b": "tata", "foo": {"bat": "baz"}}}

    def test_marshal_with_skip_none(self):
        model = OrderedDict(
            [("foo", fields.Raw), ("bat", fields.Raw), ("qux", fields.Raw)]
        )
        marshal_dict = OrderedDict([("foo", "bar"), ("bat", None)])
        output = marshal(marshal_dict, model, skip_none=True)
        assert output == {"foo": "bar"}

    def test_marshal_wildcard_with_skip_none(self):
        wild = fields.Wildcard(fields.String)
        model = OrderedDict([("foo", fields.Raw), ("*", wild)])
        marshal_dict = OrderedDict(
            [("foo", None), ("bat", None), ("baz", "biz"), ("bar", None)]
        )
        output = marshal(marshal_dict, model, skip_none=True)
        assert output == {"baz": "biz"}

    def test_marshal_decorator(self):
        model = OrderedDict([("foo", fields.Raw)])

        @marshal_with(model)
        def try_me():
            return OrderedDict([("foo", "bar"), ("bat", "baz")])

        assert try_me() == {"foo": "bar"}

    def test_marshal_decorator_with_envelope(self):
        model = OrderedDict([("foo", fields.Raw)])

        @marshal_with(model, envelope="hey")
        def try_me():
            return OrderedDict([("foo", "bar"), ("bat", "baz")])

        assert try_me() == {"hey": {"foo": "bar"}}

    def test_marshal_decorator_with_skip_none(self):
        model = OrderedDict(
            [("foo", fields.Raw), ("bat", fields.Raw), ("qux", fields.Raw)]
        )

        @marshal_with(model, skip_none=True)
        def try_me():
            return OrderedDict([("foo", "bar"), ("bat", None)])

        assert try_me() == {"foo": "bar"}

    def test_marshal_decorator_tuple(self):
        model = OrderedDict([("foo", fields.Raw)])

        @marshal_with(model)
        def try_me():
            headers = {"X-test": 123}
            return OrderedDict([("foo", "bar"), ("bat", "baz")]), 200, headers

        assert try_me() == ({"foo": "bar"}, 200, {"X-test": 123})

    def test_marshal_decorator_tuple_with_envelope(self):
        model = OrderedDict([("foo", fields.Raw)])

        @marshal_with(model, envelope="hey")
        def try_me():
            headers = {"X-test": 123}
            return OrderedDict([("foo", "bar"), ("bat", "baz")]), 200, headers

        assert try_me() == ({"hey": {"foo": "bar"}}, 200, {"X-test": 123})

    def test_marshal_decorator_tuple_with_skip_none(self):
        model = OrderedDict(
            [("foo", fields.Raw), ("bat", fields.Raw), ("qux", fields.Raw)]
        )

        @marshal_with(model, skip_none=True)
        def try_me():
            headers = {"X-test": 123}
            return OrderedDict([("foo", "bar"), ("bat", None)]), 200, headers

        assert try_me() == ({"foo": "bar"}, 200, {"X-test": 123})

    def test_marshal_field_decorator(self):
        model = fields.Raw

        @marshal_with_field(model)
        def try_me():
            return "foo"

        assert try_me() == "foo"

    def test_marshal_field_decorator_tuple(self):
        model = fields.Raw

        @marshal_with_field(model)
        def try_me():
            return "foo", 200, {"X-test": 123}

        assert try_me() == ("foo", 200, {"X-test": 123})

    def test_marshal_field(self):
        model = OrderedDict({"foo": fields.Raw()})
        marshal_fields = OrderedDict([("foo", "bar"), ("bat", "baz")])
        output = marshal(marshal_fields, model)
        assert output == {"foo": "bar"}

    def test_marshal_tuple(self):
        model = OrderedDict({"foo": fields.Raw})
        marshal_fields = OrderedDict([("foo", "bar"), ("bat", "baz")])
        output = marshal((marshal_fields,), model)
        assert output == [{"foo": "bar"}]

    def test_marshal_tuple_with_envelope(self):
        model = OrderedDict({"foo": fields.Raw})
        marshal_fields = OrderedDict([("foo", "bar"), ("bat", "baz")])
        output = marshal((marshal_fields,), model, envelope="hey")
        assert output == {"hey": [{"foo": "bar"}]}

    def test_marshal_tuple_with_skip_none(self):
        model = OrderedDict(
            [("foo", fields.Raw), ("bat", fields.Raw), ("qux", fields.Raw)]
        )
        marshal_fields = OrderedDict([("foo", "bar"), ("bat", None)])
        output = marshal((marshal_fields,), model, skip_none=True)
        assert output == [{"foo": "bar"}]

    def test_marshal_nested(self):
        model = {
            "foo": fields.Raw,
            "fee": fields.Nested({"fye": fields.String}),
        }

        marshal_fields = {
            "foo": "bar",
            "bat": "baz",
            "fee": {"fye": "fum"},
        }
        expected = {
            "foo": "bar",
            "fee": {"fye": "fum"},
        }

        output = marshal(marshal_fields, model)

        assert output == expected

    def test_marshal_ordered(self):
        model = OrderedDict(
            [("foo", fields.Raw), ("baz", fields.Raw), ("bar", fields.Raw)]
        )
        marshal_fields = {"foo": 1, "baz": 2, "bar": 3}
        expected_ordered = OrderedDict([("foo", 1), ("baz", 2), ("bar", 3)])
        ordered_output = marshal(marshal_fields, model, ordered=True)
        assert ordered_output == expected_ordered
        unordered_output = marshal(marshal_fields, model)
        assert not isinstance(unordered_output, OrderedDict)

    def test_marshal_nested_ordered(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        {
                            "fye": fields.String,
                        }
                    ),
                ),
            ]
        )

        marshal_fields = {
            "foo": "bar",
            "bat": "baz",
            "fee": {"fye": "fum"},
        }
        expected = OrderedDict([("foo", "bar"), ("fee", OrderedDict([("fye", "fum")]))])

        output = marshal(marshal_fields, model, ordered=True)

        assert isinstance(output, OrderedDict)
        assert output == expected
        assert isinstance(output["fee"], OrderedDict)

    def test_marshal_nested_with_non_null(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict([("fye", fields.String), ("blah", fields.String)]),
                        allow_null=False,
                    ),
                ),
            ]
        )
        marshal_fields = [OrderedDict([("foo", "bar"), ("bat", "baz"), ("fee", None)])]
        output = marshal(marshal_fields, model)
        expected = [
            OrderedDict(
                [("foo", "bar"), ("fee", OrderedDict([("fye", None), ("blah", None)]))]
            )
        ]
        assert output == expected

    def test_marshal_nested_with_null(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict([("fye", fields.String), ("blah", fields.String)]),
                        allow_null=True,
                    ),
                ),
            ]
        )
        marshal_fields = OrderedDict([("foo", "bar"), ("bat", "baz"), ("fee", None)])
        output = marshal(marshal_fields, model)
        expected = OrderedDict([("foo", "bar"), ("fee", None)])
        assert output == expected

    def test_marshal_nested_with_skip_none(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict([("fye", fields.String)]), skip_none=True
                    ),
                ),
            ]
        )
        marshal_fields = OrderedDict([("foo", "bar"), ("bat", "baz"), ("fee", None)])
        output = marshal(marshal_fields, model, skip_none=True)
        expected = OrderedDict([("foo", "bar")])
        assert output == expected

    def test_allow_null_presents_data(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict([("fye", fields.String), ("blah", fields.String)]),
                        allow_null=True,
                    ),
                ),
            ]
        )
        marshal_fields = OrderedDict(
            [("foo", "bar"), ("bat", "baz"), ("fee", {"blah": "cool"})]
        )
        output = marshal(marshal_fields, model)
        expected = OrderedDict(
            [("foo", "bar"), ("fee", OrderedDict([("fye", None), ("blah", "cool")]))]
        )
        assert output == expected

    def test_skip_none_presents_data(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict(
                            [
                                ("fye", fields.String),
                                ("blah", fields.String),
                                ("foe", fields.String),
                            ]
                        ),
                        skip_none=True,
                    ),
                ),
            ]
        )
        marshal_fields = OrderedDict(
            [("foo", "bar"), ("bat", "baz"), ("fee", {"blah": "cool", "foe": None})]
        )
        output = marshal(marshal_fields, model)
        expected = OrderedDict(
            [("foo", "bar"), ("fee", OrderedDict([("blah", "cool")]))]
        )
        assert output == expected

    def test_marshal_nested_property(self):
        class TestObject(object):
            @property
            def fee(self):
                return {"blah": "cool"}

        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict([("fye", fields.String), ("blah", fields.String)]),
                        allow_null=True,
                    ),
                ),
            ]
        )
        obj = TestObject()
        obj.foo = "bar"
        obj.bat = "baz"
        output = marshal([obj], model)
        expected = [
            OrderedDict(
                [
                    ("foo", "bar"),
                    ("fee", OrderedDict([("fye", None), ("blah", "cool")])),
                ]
            )
        ]
        assert output == expected

    def test_marshal_nested_property_with_skip_none(self):
        class TestObject(object):
            @property
            def fee(self):
                return {"blah": "cool", "foe": None}

        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "fee",
                    fields.Nested(
                        OrderedDict(
                            [
                                ("fye", fields.String),
                                ("blah", fields.String),
                                ("foe", fields.String),
                            ]
                        ),
                        skip_none=True,
                    ),
                ),
            ]
        )
        obj = TestObject()
        obj.foo = "bar"
        obj.bat = "baz"
        output = marshal([obj], model)
        expected = [
            OrderedDict([("foo", "bar"), ("fee", OrderedDict([("blah", "cool")]))])
        ]
        assert output == expected

    def test_marshal_list(self):
        model = OrderedDict([("foo", fields.Raw), ("fee", fields.List(fields.String))])
        marshal_fields = OrderedDict(
            [("foo", "bar"), ("bat", "baz"), ("fee", ["fye", "fum"])]
        )
        output = marshal(marshal_fields, model)
        expected = OrderedDict([("foo", "bar"), ("fee", (["fye", "fum"]))])
        assert output == expected

    def test_marshal_list_of_nesteds(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                ("fee", fields.List(fields.Nested({"fye": fields.String}))),
            ]
        )
        marshal_fields = OrderedDict(
            [("foo", "bar"), ("bat", "baz"), ("fee", {"fye": "fum"})]
        )
        output = marshal(marshal_fields, model)
        expected = OrderedDict(
            [("foo", "bar"), ("fee", [OrderedDict([("fye", "fum")])])]
        )
        assert output == expected

    def test_marshal_list_of_lists(self):
        model = OrderedDict(
            [("foo", fields.Raw), ("fee", fields.List(fields.List(fields.String)))]
        )
        marshal_fields = OrderedDict(
            [("foo", "bar"), ("bat", "baz"), ("fee", [["fye"], ["fum"]])]
        )
        output = marshal(marshal_fields, model)
        expected = OrderedDict([("foo", "bar"), ("fee", [["fye"], ["fum"]])])
        assert output == expected

    def test_marshal_nested_dict(self):
        model = OrderedDict(
            [
                ("foo", fields.Raw),
                (
                    "bar",
                    OrderedDict(
                        [
                            ("a", fields.Raw),
                            ("b", fields.Raw),
                        ]
                    ),
                ),
            ]
        )
        marshal_fields = OrderedDict(
            [
                ("foo", "foo-val"),
                ("bar", "bar-val"),
                ("bat", "bat-val"),
                ("a", 1),
                ("b", 2),
                ("c", 3),
            ]
        )
        output = marshal(marshal_fields, model)
        expected = OrderedDict(
            [("foo", "foo-val"), ("bar", OrderedDict([("a", 1), ("b", 2)]))]
        )
        assert output == expected

    @pytest.mark.options(debug=True)
    def test_will_prettyprint_json_in_debug_mode(self, app, client):
        api = Api(app)

        class Foo1(Resource):
            def get(self):
                return {"foo": "bar", "baz": "asdf"}

        api.add_resource(Foo1, "/foo", endpoint="bar")

        foo = client.get("/foo")

        # Python's dictionaries have random order (as of "new" Pythons,
        # anyway), so we can't verify the actual output here.  We just
        # assert that they're properly prettyprinted.
        lines = foo.data.splitlines()
        lines = [line.decode() for line in lines]
        assert "{" == lines[0]
        assert lines[1].startswith("    ")
        assert lines[2].startswith("    ")
        assert "}" == lines[3]

        # Assert our trailing newline.
        assert foo.data.endswith(b"\n")

    def test_json_float_marshalled(self, app, client):
        api = Api(app)

        class FooResource(Resource):
            fields = {"foo": fields.Float}

            def get(self):
                return marshal({"foo": 3.0}, self.fields)

        api.add_resource(FooResource, "/api")

        resp = client.get("/api")
        assert resp.status_code == 200
        assert resp.data.decode("utf-8") == '{"foo": 3.0}\n'
