"""Per-event greeting overrides, through the routes a host and a guest actually touch.

`test_greeting.py` covers the resolution order as a unit. This file covers the half that
unit test cannot: that the admin route stores what it was given under the same rules the
wedding-wide route enforces, and that what a guest reads at their token actually changes as
a result. A resolver with a perfect fallback chain is worthless if the PATCH never writes the
column or the invitation route never passes the event in.
"""

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"),
]


async def _patch_messages(
    client: AsyncClient, event_id: Any, messages: dict[str, dict[str, str]]
) -> Any:
    return await client.patch(
        f"/api/admin/events/{event_id}", json={"invitation_messages": messages}
    )


async def test_an_event_override_reaches_that_events_guests(
    fixture_event: dict[str, Any],
) -> None:
    token = f"tok-{fixture_event['tag']}-0"
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        # Before: nothing stored on the event, so the built-in default is what a guest reads.
        before = await client.get(f"/api/invitations/by-token/{token}")
        assert before.status_code == 200, before.text
        assert before.json()["greeting"]["message_en"] == "You are cordially invited"

        patched = await _patch_messages(
            client, fixture_event["event_id"], {"en": {"single": "Join us at the Walima"}}
        )
        assert patched.status_code == 200, patched.text
        assert patched.json()["event"]["invitation_messages"] == {
            "en": {"single": "Join us at the Walima"}
        }

        after = await client.get(f"/api/invitations/by-token/{token}")
        assert after.json()["greeting"]["message_en"] == "Join us at the Walima"


async def test_an_english_override_does_not_change_what_a_bangla_reader_sees(
    fixture_event: dict[str, Any],
) -> None:
    """Only English is editable per event, so Bangla must stay on its own fallback chain."""
    token = f"tok-{fixture_event['tag']}-0"
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        await _patch_messages(
            client, fixture_event["event_id"], {"en": {"single": "Join us at the Walima"}}
        )
        body = (await client.get(f"/api/invitations/by-token/{token}")).json()

    assert body["greeting"]["message_bn"] == "আপনাকে সাদর আমন্ত্রণ"


async def test_clearing_the_override_returns_the_event_to_inheriting(
    fixture_event: dict[str, Any],
) -> None:
    token = f"tok-{fixture_event['tag']}-0"
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        await _patch_messages(
            client, fixture_event["event_id"], {"en": {"single": "Join us at the Walima"}}
        )
        cleared = await _patch_messages(client, fixture_event["event_id"], {})
        assert cleared.status_code == 200, cleared.text
        assert cleared.json()["event"]["invitation_messages"] == {}

        body = (await client.get(f"/api/invitations/by-token/{token}")).json()

    assert body["greeting"]["message_en"] == "You are cordially invited"


async def test_the_event_route_enforces_the_same_length_cap(
    fixture_event: dict[str, Any],
) -> None:
    """A route that forgot to call `validate_messages` would pass every unit test above."""
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await _patch_messages(
            client, fixture_event["event_id"], {"en": {"single": "x" * 101}}
        )

    assert response.status_code == 422
    assert "100" in response.json()["detail"]


async def test_the_event_route_strips_markup_like_the_wedding_route(
    fixture_event: dict[str, Any],
) -> None:
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await _patch_messages(
            client,
            fixture_event["event_id"],
            {"en": {"family": "<script>alert(1)</script>Bring everyone"}},
        )

    assert response.status_code == 200, response.text
    stored = response.json()["event"]["invitation_messages"]["en"]["family"]
    assert stored == "alert(1)Bring everyone"
    assert "<" not in stored


async def test_an_event_created_with_messages_stores_them_cleaned() -> None:
    """The create path writes the same column, so it needs the same rules (task 5.5)."""
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post(
            "/api/admin/events",
            json={
                "type": "walima",
                "title_en": "Message Test Walima",
                "title_bn": "ওয়ালিমা",
                "starts_at": "2027-01-15T13:30:00Z",
                "venue_name": "Test Hall",
                "venue_address": "Dhaka",
                # Required since add-event-host-contact; incidental to what this test asserts.
                "host_name_1": "Abdul Karim",
                "host_phone": "+8801711223344",
                "invitation_messages": {"en": {"single": "<b>Join us</b>", "couple": "?"}},
            },
        )
        assert response.status_code == 201, response.text
        created = response.json()
        # Markup stripped, and the unknown invitation type dropped rather than stored.
        assert created["invitation_messages"] == {"en": {"single": "Join us"}}

        # Leaves the database as it found it — this one does not ride on fixture_event.
        await client.delete(f"/api/admin/events/{created['id']}")
