import dataclasses

import pytest

from domain.addresses.user_address import UserAddress
from domain.postcode import Postcode


def test_user_address_holds_postcode_value_object() -> None:
    # act
    addr = UserAddress(user_address="1 The Street", postcode=Postcode("sw1a 1aa"))
    # assert
    assert addr.postcode == Postcode("SW1A1AA")


def test_user_address_preserves_user_address_verbatim() -> None:
    # The free-text user_address string is intentionally NOT normalised --
    # only the postcode is canonicalised, and that happens inside Postcode.
    # act
    addr = UserAddress(
        user_address="  1 The   Street  ", postcode=Postcode("SW1A1AA")
    )
    # assert
    assert addr.user_address == "  1 The   Street  "


def test_user_address_internal_reference_defaults_to_none() -> None:
    # act
    addr = UserAddress(user_address="1 The Street", postcode=Postcode("SW1A1AA"))
    # assert
    assert addr.internal_reference is None


def test_user_address_internal_reference_accepted() -> None:
    # act
    addr = UserAddress(
        user_address="1 The Street",
        postcode=Postcode("SW1A1AA"),
        internal_reference="cust-42",
    )
    # assert
    assert addr.internal_reference == "cust-42"


def test_user_address_is_frozen() -> None:
    # arrange
    addr = UserAddress(user_address="1 The Street", postcode=Postcode("SW1A1AA"))
    # act / assert
    with pytest.raises(dataclasses.FrozenInstanceError):
        addr.postcode = Postcode("OTHER")  # type: ignore[misc]


def test_user_address_equality_uses_canonical_postcode() -> None:
    # Postcode sanitises eagerly, so addresses built from different surface
    # forms of the same postcode compare equal.
    # arrange
    a = UserAddress(user_address="1 The Street", postcode=Postcode("sw1a 1aa"))
    b = UserAddress(user_address="1 The Street", postcode=Postcode("SW1A1AA"))
    # act / assert
    assert a == b


def test_user_address_source_row_defaults_to_empty_dict() -> None:
    # act
    addr = UserAddress(user_address="1 The Street", postcode=Postcode("SW1A1AA"))
    # assert
    assert addr.source_row == {}


def test_user_address_carries_source_row() -> None:
    # arrange
    row = {"Address 1": "1 The Street", "postcode": "SW1A 1AA", "SAP Score": "72"}
    # act
    addr = UserAddress(
        user_address="1 The Street",
        postcode=Postcode("SW1A 1AA"),
        source_row=row,
    )
    # assert
    assert addr.source_row == row


def test_user_address_equality_ignores_source_row() -> None:
    # source_row is excluded from equality (and hashing): identity stays
    # defined by the parsed fields.
    # arrange
    a = UserAddress(
        user_address="1 The Street",
        postcode=Postcode("SW1A1AA"),
        source_row={"x": "1"},
    )
    b = UserAddress(
        user_address="1 The Street",
        postcode=Postcode("SW1A1AA"),
        source_row={"y": "2"},
    )
    # act / assert
    assert a == b
