"""Unit tests for the dynojson marshall function."""

import json

import pytest

from dynojson import marshall, unmarshall

from .fixtures import (
    MARSHALLED_OBJECT,
    MARSHALLED_SCAN,
    UNMARSHALLED_ARRAY,
    UNMARSHALLED_ARRAY_ARRAYS,
    UNMARSHALLED_OBJECT,
    UNMARSHALLED_SCAN,
)


class TestMarshall:
    """Tests for converting regular JSON to DynamoDB JSON."""

    def test_marshall_single_object(self):
        result = json.loads(marshall(UNMARSHALLED_OBJECT))
        expected = json.loads(MARSHALLED_OBJECT)
        assert result == expected

    def test_marshall_array_of_objects_roundtrip(self):
        """Marshall then unmarshall should return the original array."""
        marshalled = marshall(UNMARSHALLED_ARRAY)
        result = json.loads(unmarshall(marshalled))
        expected = json.loads(UNMARSHALLED_ARRAY)
        assert result == expected

    def test_marshall_scan_result(self):
        result = json.loads(marshall(UNMARSHALLED_SCAN))
        expected = json.loads(MARSHALLED_SCAN)
        assert result == expected

    def test_marshall_array_of_arrays_roundtrip(self):
        """Marshall then unmarshall should return the original nested arrays."""
        marshalled = marshall(UNMARSHALLED_ARRAY_ARRAYS)
        result = json.loads(unmarshall(marshalled))
        expected = json.loads(UNMARSHALLED_ARRAY_ARRAYS)
        assert result == expected

    def test_marshall_string(self):
        result = json.loads(marshall('{"key":"value"}'))
        assert result == {"key": {"S": "value"}}

    def test_marshall_number(self):
        result = json.loads(marshall('{"key":42}'))
        assert result == {"key": {"N": "42"}}

    def test_marshall_boolean(self):
        result = json.loads(marshall('{"key":true}'))
        assert result == {"key": {"BOOL": True}}

    def test_marshall_null(self):
        result = json.loads(marshall('{"key":null}'))
        assert result == {"key": {"NULL": True}}

    def test_marshall_nested(self):
        input_json = '{"a":{"b":[1,2]}}'
        result = json.loads(marshall(input_json))
        assert result == {"a": {"M": {"b": {"L": [{"N": "1"}, {"N": "2"}]}}}}

    def test_marshall_invalid_json_raises(self):
        with pytest.raises(ValueError):
            marshall("not json")

    # ── Empty values ────────────────────────────────────────────────────

    def test_marshall_empty_string(self):
        result = json.loads(marshall('{"key":""}'))
        assert result == {"key": {"S": ""}}

    def test_marshall_empty_array(self):
        result = json.loads(marshall('{"key":[]}'))
        assert result == {"key": {"L": []}}

    def test_marshall_empty_object(self):
        result = json.loads(marshall('{"key":{}}'))
        assert result == {"key": {"M": {}}}

    # ── Number edge cases ───────────────────────────────────────────────

    def test_marshall_negative_number(self):
        result = json.loads(marshall('{"key":-42}'))
        assert result == {"key": {"N": "-42"}}

    def test_marshall_zero(self):
        result = json.loads(marshall('{"key":0}'))
        assert result == {"key": {"N": "0"}}

    # ── Mixed / complex ─────────────────────────────────────────────────

    def test_marshall_mixed_type_array(self):
        input_json = json.dumps({"key": ["hello", 42, True, None, {"k": "v"}, [1, 2]]})
        result = json.loads(marshall(input_json))
        assert result == {"key": {"L": [
            {"S": "hello"}, {"N": "42"}, {"BOOL": True},
            {"NULL": True}, {"M": {"k": {"S": "v"}}},
            {"L": [{"N": "1"}, {"N": "2"}]}
        ]}}

    def test_marshall_all_json_types(self):
        """Marshall every JSON type in a single Item."""
        original = json.dumps({
            "str": "hello", "num": 42, "flt": 3.14,
            "yes": True, "no": False, "nul": None,
            "arr": [1, "two"], "obj": {"nested": True}
        })
        result = json.loads(marshall(original))
        assert result == {
            "str": {"S": "hello"}, "num": {"N": "42"}, "flt": {"N": "3.14"},
            "yes": {"BOOL": True}, "no": {"BOOL": False}, "nul": {"NULL": True},
            "arr": {"L": [{"N": "1"}, {"S": "two"}]},
            "obj": {"M": {"nested": {"BOOL": True}}}
        }
