from datatypes.magicplan.api.response import PlanSummary
from backend.magic_plan.address_matcher import find_matching_plan, _extract_postcode


def _make_plan(
    plan_id: str,
    street: str | None = None,
    street_number: str | None = None,
    postal_code: str | None = None,
) -> PlanSummary:
    return PlanSummary.model_validate(
        {
            "id": plan_id,
            "name": f"Plan {plan_id}",
            "address": {
                "street": street,
                "street_number": street_number,
                "postal_code": postal_code,
            },
        }
    )


# --- _extract_postcode ---


def test_extract_postcode_standard_format() -> None:
    assert _extract_postcode("2 Laburnum Way Bromley BR2 8BZ") == "BR28BZ"


def test_extract_postcode_no_space_in_postcode() -> None:
    assert _extract_postcode("123 High St London SW1A1AA") == "SW1A1AA"


def test_extract_postcode_lowercase_input() -> None:
    assert _extract_postcode("2 laburnum way br2 8bz") == "BR28BZ"


def test_extract_postcode_none_when_absent() -> None:
    assert _extract_postcode("123 High Street London") is None


def test_extract_postcode_none_for_empty_string() -> None:
    assert _extract_postcode("") is None


# --- find_matching_plan ---


PLAN_A = _make_plan(
    "plan-a", street="Laburnum Way", street_number="2", postal_code="BR2 8BZ"
)
PLAN_B = _make_plan(
    "plan-b", street="Station Road", street_number="11", postal_code="BR1 3LP"
)


def test_find_matching_plan_returns_match() -> None:
    # Arrange
    plans = [PLAN_A, PLAN_B]
    # Act
    result = find_matching_plan(plans, "2 Laburnum Way Bromley BR2 8BZ")
    # Assert
    assert result is not None
    assert result.id == "plan-a"


def test_find_matching_plan_postcode_mismatch_returns_none() -> None:
    # Arrange
    plans = [PLAN_A]
    # Act
    result = find_matching_plan(plans, "2 Laburnum Way Bromley SW1A 1AA")
    # Assert
    assert result is None


def test_find_matching_plan_street_mismatch_returns_none() -> None:
    # Arrange
    plans = [PLAN_A]
    # Act
    result = find_matching_plan(plans, "99 Other Road Bromley BR2 8BZ")
    # Assert
    assert result is None


def test_find_matching_plan_empty_list_returns_none() -> None:
    # Act
    result = find_matching_plan([], "2 Laburnum Way Bromley BR2 8BZ")
    # Assert
    assert result is None


def test_find_matching_plan_postcode_with_no_space_in_address() -> None:
    # Arrange - address has postcode without internal space
    plans = [PLAN_A]
    # Act
    result = find_matching_plan(plans, "2 Laburnum Way Bromley BR28BZ")
    # Assert
    assert result is not None
    assert result.id == "plan-a"


def test_find_matching_plan_plan_postcode_with_no_space() -> None:
    # Arrange - plan has postcode without space
    plan = _make_plan(
        "plan-c", street="Laburnum Way", street_number="2", postal_code="BR28BZ"
    )
    # Act
    result = find_matching_plan([plan], "2 Laburnum Way Bromley BR2 8BZ")
    # Assert
    assert result is not None
    assert result.id == "plan-c"


def test_find_matching_plan_no_postcode_in_address_returns_none() -> None:
    # Act
    result = find_matching_plan([PLAN_A], "2 Laburnum Way Bromley")
    # Assert
    assert result is None


def test_find_matching_plan_second_plan_matches() -> None:
    # Arrange
    plans = [PLAN_A, PLAN_B]
    # Act
    result = find_matching_plan(plans, "11 Station Road Bromley BR1 3LP")
    # Assert
    assert result is not None
    assert result.id == "plan-b"
