"""Tests for DynamodbAdapter."""

from __future__ import annotations

from decimal import Decimal

from db_test_helpers.dynamodb._adapter import _ROOT_PK, DynamodbAdapter


class TestBuildChildPath:
    def test_root_level(self):
        adapter = DynamodbAdapter(None)
        result = adapter.build_child_path("", "users", {"__document_id__": "user-1", "name": "Alice"})
        assert result == "users/user-1"

    def test_nested(self):
        adapter = DynamodbAdapter(None)
        result = adapter.build_child_path("users/user-1", "posts", {"__document_id__": "post-1", "title": "Hello"})
        assert result == "users/user-1/posts/post-1"


class TestWriteRecord:
    def test_write_with_doc_id(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record("users", "", {"__document_id__": "user-1", "name": "Alice"})

        assert result["__document_id__"] == "user-1"
        assert result["name"] == "Alice"

        table = dynamodb.Table("users")
        item = table.get_item(Key={"_pk": _ROOT_PK, "_sk": "user-1"})["Item"]
        assert item["name"] == "Alice"
        assert "__document_id__" not in item

    def test_write_without_doc_id_auto_generates(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record("users", "", {"name": "Bob"})

        assert "__document_id__" in result
        assert result["__document_id__"] is not None
        assert len(result["__document_id__"]) > 0
        assert result["name"] == "Bob"

    def test_write_with_none_doc_id_auto_generates(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record("users", "", {"__document_id__": None, "name": "Carol"})

        assert result["__document_id__"] is not None
        assert len(result["__document_id__"]) > 0

    def test_write_with_parent_path(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record("users", "org/org-1", {"__document_id__": "user-1", "name": "Alice"})

        assert result["__document_id__"] == "user-1"

        table = dynamodb.Table("users")
        item = table.get_item(Key={"_pk": "org/org-1", "_sk": "user-1"})["Item"]
        assert item["name"] == "Alice"

    def test_write_float_to_decimal(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record("users", "", {"__document_id__": "user-1", "score": 3.14})

        table = dynamodb.Table("users")
        item = table.get_item(Key={"_pk": _ROOT_PK, "_sk": "user-1"})["Item"]
        assert isinstance(item["score"], Decimal)

        # Return value should be float, not Decimal
        assert isinstance(result["score"], float)
        assert result["score"] == 3.14

    def test_write_nested_structure(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record(
            "users",
            "",
            {
                "__document_id__": "user-1",
                "meta": {"score": 1.5, "tags": ["a", "b"]},
                "scores": [1.1, 2.2],
            },
        )

        assert result["meta"]["score"] == 1.5
        assert result["meta"]["tags"] == ["a", "b"]
        assert result["scores"] == [1.1, 2.2]

    def test_write_doc_id_not_stored_as_field(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("users", "", {"__document_id__": "user-1", "name": "Alice"})

        table = dynamodb.Table("users")
        item = table.get_item(Key={"_pk": _ROOT_PK, "_sk": "user-1"})["Item"]
        assert "__document_id__" not in item

    def test_write_int_stays_int(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.write_record("users", "", {"__document_id__": "user-1", "age": 30})

        assert isinstance(result["age"], int)
        assert result["age"] == 30


class TestReadRecords:
    def test_read_empty(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        result = adapter.read_records("users", "")
        assert result == []

    def test_read_records(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("users", "", {"__document_id__": "user-1", "name": "Alice", "age": 30})
        adapter.write_record("users", "", {"__document_id__": "user-2", "name": "Bob", "age": 25})

        result = adapter.read_records("users", "")
        assert len(result) == 2

        ids = {r["__document_id__"] for r in result}
        assert ids == {"user-1", "user-2"}

        for r in result:
            assert "__document_id__" in r
            assert "name" in r

    def test_read_with_parent_path(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("posts", "users/user-1", {"__document_id__": "post-1", "title": "Hello"})

        result = adapter.read_records("posts", "users/user-1")
        assert len(result) == 1
        assert result[0]["__document_id__"] == "post-1"
        assert result[0]["title"] == "Hello"

    def test_read_filters_by_parent_path(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("posts", "users/user-1", {"__document_id__": "post-1", "title": "Hello"})
        adapter.write_record("posts", "users/user-2", {"__document_id__": "post-2", "title": "World"})

        result = adapter.read_records("posts", "users/user-1")
        assert len(result) == 1
        assert result[0]["__document_id__"] == "post-1"

    def test_read_number_roundtrip(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("users", "", {"__document_id__": "user-1", "score": 3.14, "age": 30})

        result = adapter.read_records("users", "")
        assert isinstance(result[0]["score"], float)
        assert result[0]["score"] == 3.14
        assert isinstance(result[0]["age"], int)
        assert result[0]["age"] == 30

    def test_read_excludes_pk_sk(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("users", "", {"__document_id__": "user-1", "name": "Alice"})

        result = adapter.read_records("users", "")
        assert "_pk" not in result[0]
        assert "_sk" not in result[0]


class TestClearGroup:
    def test_clear_group(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("users", "", {"__document_id__": "user-1", "name": "Alice"})
        adapter.write_record("users", "", {"__document_id__": "user-2", "name": "Bob"})

        adapter.clear_group("users", "")
        assert adapter.read_records("users", "") == []

    def test_clear_group_with_parent_path(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("posts", "users/user-1", {"__document_id__": "post-1", "title": "Hello"})

        adapter.clear_group("posts", "users/user-1")
        assert adapter.read_records("posts", "users/user-1") == []

    def test_clear_group_preserves_other_parent_path(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb)
        adapter.write_record("posts", "users/user-1", {"__document_id__": "post-1", "title": "Hello"})
        adapter.write_record("posts", "users/user-2", {"__document_id__": "post-2", "title": "World"})

        adapter.clear_group("posts", "users/user-1")
        assert adapter.read_records("posts", "users/user-1") == []

        remaining = adapter.read_records("posts", "users/user-2")
        assert len(remaining) == 1
        assert remaining[0]["__document_id__"] == "post-2"


class TestCustomKeyAttrs:
    def test_write_and_read_with_custom_keys(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb, pk_attr="PK", sk_attr="SK")
        adapter.write_record("custom_keys", "", {"__document_id__": "item-1", "name": "Alice"})

        records = adapter.read_records("custom_keys", "")
        assert len(records) == 1
        assert records[0]["__document_id__"] == "item-1"
        assert records[0]["name"] == "Alice"
        assert "PK" not in records[0]
        assert "SK" not in records[0]

    def test_clear_with_custom_keys(self, dynamodb):
        adapter = DynamodbAdapter(dynamodb, pk_attr="PK", sk_attr="SK")
        adapter.write_record("custom_keys", "", {"__document_id__": "item-1", "name": "Alice"})
        adapter.write_record("custom_keys", "", {"__document_id__": "item-2", "name": "Bob"})

        adapter.clear_group("custom_keys", "")
        assert adapter.read_records("custom_keys", "") == []
