from typing import Any
from unittest.mock import MagicMock, patch

import pytest
from pydantic import ValidationError

from backend.magic_plan.handler import handler

ADDRESS = "2 Laburnum Way Bromley BR2 8BZ"
PLAN_UID = "a7285ed1-878d-47eb-8aa6-85ef9e187516"


def _make_settings(**overrides: str) -> MagicMock:
    settings = MagicMock()
    settings.MAGICPLAN_CUSTOMER_ID = overrides.get("customer_id", "cust-123")
    settings.MAGICPLAN_API_KEY = overrides.get("api_key", "key-abc")
    return settings


def _call_handler(body: dict[str, Any]) -> Any:
    return handler.__wrapped__(body, None)  # type: ignore[attr-defined]


@pytest.fixture()
def mock_plan() -> MagicMock:
    plan = MagicMock()
    plan.uid = PLAN_UID
    return plan


@pytest.fixture()
def mock_service(mock_plan: MagicMock) -> MagicMock:
    service = MagicMock()
    service.run.return_value = mock_plan
    return service


# --- request validation ---


def test_handler_raises_on_missing_address(mock_plan: MagicMock) -> None:
    # Arrange
    body: dict[str, Any] = {}
    with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \
         patch("backend.magic_plan.handler.MagicPlanClient"), \
         patch("backend.magic_plan.handler.MagicPlanService"):
        # Act / Assert
        with pytest.raises(ValidationError):
            _call_handler(body)


# --- client construction ---


def test_handler_constructs_client_from_settings(mock_service: MagicMock) -> None:
    # Arrange
    body = {"address": ADDRESS, "hubspot_deal_id": "deal-123"}
    with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings(customer_id="cust-xyz", api_key="key-xyz")), \
         patch("backend.magic_plan.handler.MagicPlanClient") as MockClient, \
         patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service):
        # Act
        _call_handler(body)
    # Assert
    MockClient.assert_called_once_with(customer_id="cust-xyz", api_key="key-xyz")


# --- service orchestration ---


def test_handler_calls_service_run_with_address(mock_service: MagicMock) -> None:
    # Arrange
    body = {"address": ADDRESS, "hubspot_deal_id": "deal-123"}
    with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \
         patch("backend.magic_plan.handler.MagicPlanClient"), \
         patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service):
        # Act
        _call_handler(body)
    # Assert
    mock_service.run.assert_called_once()
    request = mock_service.run.call_args.args[0]
    assert request.address == ADDRESS
    assert request.uprn is None


def test_handler_passes_uprn_to_service(mock_service: MagicMock) -> None:
    # Arrange
    body = {"address": ADDRESS, "uprn": "100023336956", "hubspot_deal_id": "deal-123"}
    with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \
         patch("backend.magic_plan.handler.MagicPlanClient"), \
         patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service):
        # Act
        _call_handler(body)
    # Assert
    mock_service.run.assert_called_once()
    request = mock_service.run.call_args.args[0]
    assert request.address == ADDRESS
    assert request.uprn == "100023336956"


def test_handler_returns_plan_uid(mock_service: MagicMock) -> None:
    # Arrange
    body = {"address": ADDRESS, "hubspot_deal_id": "deal-123"}
    with patch("backend.magic_plan.handler.get_settings", return_value=_make_settings()), \
         patch("backend.magic_plan.handler.MagicPlanClient"), \
         patch("backend.magic_plan.handler.MagicPlanService", return_value=mock_service):
        # Act
        result = _call_handler(body)
    # Assert
    assert result == PLAN_UID
