"""The host of an event: required, normalised, per-event, and reaching the invitation.

Four things are worth a test here and one of them is not obvious.

The obvious three: the required half is actually refused when missing, a phone typed the way
a Bangladeshi admin types it comes back in E.164, and "no second host" has exactly one
spelling in the database.

The fourth is that host details do not leak between events of one wedding. That is the whole
reason the columns live on `event` rather than on `wedding` (design D1), and it is the kind
of invariant that survives review and then quietly breaks when someone adds a convenience
lookup that falls back to the wedding.
"""

from typing import Any

import pytest
from httpx import ASGITransport, AsyncClient

from app.main import app

from .conftest import requires_db

pytestmark = [
    requires_db,
    pytest.mark.usefixtures("app_engine_per_loop", "super_admin"),
]

#: Everything a create needs apart from the host, so each test can vary only the host half
#: and a missing field is visibly the one under test.
BASE_EVENT: dict[str, Any] = {
    "type": "walima",
    "title_en": "Host Test Walima",
    "title_bn": "ওয়ালিমা",
    "starts_at": "2026-12-01T12:00:00+00:00",
    "venue_name": "Rose Garden",
    "venue_address": "Dhaka",
}


def _payload(**host: Any) -> dict[str, Any]:
    return {**BASE_EVENT, **host}


async def _client() -> AsyncClient:
    return AsyncClient(transport=ASGITransport(app=app), base_url="http://test")


async def _delete(client: AsyncClient, event_id: str) -> None:
    await client.delete(f"/api/admin/events/{event_id}")


async def test_creating_without_a_host_name_is_refused() -> None:
    async with await _client() as client:
        response = await client.post(
            "/api/admin/events", json=_payload(host_phone="+8801711223344")
        )

    assert response.status_code == 422, response.text


async def test_creating_without_a_host_phone_is_refused() -> None:
    async with await _client() as client:
        response = await client.post("/api/admin/events", json=_payload(host_name_1="Abdul Karim"))

    assert response.status_code == 422, response.text


async def test_creating_with_both_stores_them() -> None:
    async with await _client() as client:
        response = await client.post(
            "/api/admin/events",
            json=_payload(
                host_name_1="Abdul Karim",
                host_name_2="Rahima Karim",
                host_phone="+8801711223344",
            ),
        )
        assert response.status_code == 201, response.text
        body = response.json()
        assert body["host_name_1"] == "Abdul Karim"
        assert body["host_name_2"] == "Rahima Karim"
        assert body["host_phone"] == "+8801711223344"

        await _delete(client, body["id"])


async def test_a_local_format_phone_is_stored_in_e164() -> None:
    """Typed the way a Bangladeshi admin types it, dialled the way a phone needs it."""
    async with await _client() as client:
        response = await client.post(
            "/api/admin/events",
            json=_payload(host_name_1="Abdul Karim", host_phone="01711223344"),
        )
        assert response.status_code == 201, response.text
        body = response.json()
        assert body["host_phone"] == "+8801711223344"

        await _delete(client, body["id"])


async def test_an_unparseable_phone_names_the_phone_field() -> None:
    """A form-wide "invalid input" would leave the admin guessing which of six fields."""
    async with await _client() as client:
        response = await client.post(
            "/api/admin/events",
            json=_payload(host_name_1="Abdul Karim", host_phone="not a number"),
        )

    assert response.status_code == 422, response.text
    detail = response.json()["detail"]
    assert detail["field"] == "host_phone"
    assert detail["code"] == "invalid_host_phone"


async def test_a_blank_second_host_name_is_stored_as_null() -> None:
    """Not "". Two spellings of absent means every reader has to test for both (design D3)."""
    async with await _client() as client:
        response = await client.post(
            "/api/admin/events",
            json=_payload(
                host_name_1="Abdul Karim", host_name_2="   ", host_phone="+8801711223344"
            ),
        )
        assert response.status_code == 201, response.text
        body = response.json()
        assert body["host_name_2"] is None

        await _delete(client, body["id"])


async def test_an_edit_may_not_empty_the_required_host_fields(
    fixture_event: dict[str, Any],
) -> None:
    """Creation demanded them; an edit that could blank them would make the demand pointless."""
    event_id = fixture_event["event_id"]
    async with await _client() as client:
        cleared_name = await client.patch(
            f"/api/admin/events/{event_id}", json={"host_name_1": "  "}
        )
        assert cleared_name.status_code == 422, cleared_name.text

        cleared_phone = await client.patch(f"/api/admin/events/{event_id}", json={"host_phone": ""})
        assert cleared_phone.status_code == 422, cleared_phone.text


async def test_an_edit_that_omits_the_host_leaves_it_alone(
    fixture_event: dict[str, Any],
) -> None:
    """The bug this guards: mapping an absent key to a cleared column would blank the host
    every time an admin moved the date."""
    event_id = fixture_event["event_id"]
    async with await _client() as client:
        response = await client.patch(
            f"/api/admin/events/{event_id}", json={"venue_name": "Somewhere Else"}
        )
        assert response.status_code == 200, response.text
        assert response.json()["event"]["host_name_1"] == "Abdul Karim"


async def test_two_events_of_one_wedding_hold_independent_hosts(
    fixture_event: dict[str, Any],
) -> None:
    """The reason the columns are on `event` at all (design D1)."""
    event_id = fixture_event["event_id"]
    async with await _client() as client:
        # A second event of the same wedding — created through the route, so it lands on the
        # one wedding the API resolves.
        created = await client.post(
            "/api/admin/events",
            json=_payload(
                title_en="Second Ceremony",
                host_name_1="Kamrul Hasan",
                host_name_2="Shirin Hasan",
                host_phone="+8801799999999",
            ),
        )
        assert created.status_code == 201, created.text
        second = created.json()

        first = await client.patch(
            f"/api/admin/events/{event_id}",
            json={"host_name_1": "Abdul Karim", "host_phone": "+8801711223344"},
        )
        assert first.status_code == 200, first.text

        # Neither write disturbed the other.
        assert first.json()["event"]["host_name_1"] == "Abdul Karim"
        reread = await client.get("/api/admin/events")
        by_id = {e["id"]: e for e in reread.json()}
        assert by_id[second["id"]]["host_name_1"] == "Kamrul Hasan"
        assert by_id[second["id"]]["host_phone"] == "+8801799999999"
        assert by_id[str(event_id)]["host_name_2"] is None

        await _delete(client, second["id"])


async def test_the_host_reaches_the_guests_invitation(fixture_event: dict[str, Any]) -> None:
    """Storing it is worthless if the invitation route never returns it."""
    token = f"tok-{fixture_event['tag']}-0"
    async with await _client() as client:
        await client.patch(
            f"/api/admin/events/{fixture_event['event_id']}",
            json={
                "host_name_1": "Abdul Karim",
                "host_name_2": "Rahima Karim",
                "host_phone": "01711223344",
            },
        )
        body = (await client.get(f"/api/invitations/by-token/{token}")).json()

    assert body["event"]["host_name_1"] == "Abdul Karim"
    assert body["event"]["host_name_2"] == "Rahima Karim"
    assert body["event"]["host_phone"] == "+8801711223344"
