"""The preview block in an invitation email (task 7.7, design D4, spec messaging).

Mail clients read no Open Graph tags, so this block is markup the system composes itself.
Most of these tests are about the hostile conditions it has to survive — blocked images,
stripped styling, a plain-text alternative — rather than about how it looks.

The one that guards a rule rather than an appearance is
`test_the_block_is_not_written_into_the_message_log`: the block carries the guest's name and
their token URL, so it must be built at send time and stored nowhere.
"""

import uuid
from datetime import UTC, datetime

import pytest

from app.models.enums import EventType, Locale
from app.models.wedding import Event, Wedding
from app.services import link_preview, templating

BASE = "https://wedding.example.com"
INVITE_URL = f"{BASE}/i/Xk3mQ7tokenvalue1234"


def _wedding() -> Wedding:
    return Wedding(
        id=uuid.uuid4(),
        bride_name="Nazifa",
        groom_name="Abdullah",
        slug="nazifa-abdullah",
        default_locale=Locale.EN,
        timezone="Asia/Dhaka",
    )


def _event(venue: str = "Dhaka Club") -> Event:
    return Event(
        id=uuid.uuid4(),
        type=EventType.MARRIAGE,
        slug="marriage",
        title_bn="বিবাহ",
        title_en="Marriage Ceremony",
        starts_at=datetime(2026, 12, 12, 13, 30, tzinfo=UTC),
        venue_name=venue,
        venue_address="Ramna, Dhaka",
        host_name_1="Abdul Karim",
        host_phone="+8801711223344",
    )


def _config() -> dict[str, object]:
    return {
        "preview_image": {
            "url": "/media/cards/e/abc123.png",
            "width": 1200,
            "height": 630,
            "byte_size": 120_000,
            "content_type": "image/png",
        }
    }


def _block(*, guest_name: str = "Rahim Uddin", with_image: bool = True, venue: str = "Dhaka Club"):
    preview = link_preview.for_guest(
        _event(venue),
        _wedding(),
        guest_name=guest_name,
        base_url=BASE,
        card_config=_config() if with_image else None,
    )
    return link_preview.email_block(preview, invite_url=INVITE_URL)


# ------------------------------------------------------------------ what it contains


def test_the_block_carries_the_event_the_date_and_the_venue() -> None:
    block = _block()
    assert "Marriage Ceremony" in block
    assert "12 December 2026" in block
    assert "Dhaka Club" in block


def test_the_block_links_to_the_guests_own_invitation() -> None:
    assert INVITE_URL in _block()


def test_the_block_may_name_the_guest() -> None:
    """It goes to one inbox. The guest-free rule governs the meta tags, not this."""
    assert "Rahim Uddin" in _block()


def test_the_image_carries_alt_text_naming_the_event() -> None:
    block = _block()
    assert 'alt="Marriage Ceremony — Nazifa &amp; Abdullah"' in block


def test_the_image_declares_its_absolute_url() -> None:
    assert "https://wedding.example.com/media/cards/e/abc123.png" in _block()


# ------------------------------------------------------------------ hostile mail clients


def test_with_no_image_the_words_survive() -> None:
    """Most clients block remote images by default, so this is the common case, not the
    edge one — and an event with no published picture hits the same path."""
    block = _block(with_image=False)
    assert "<img" not in block
    assert "Marriage Ceremony" in block
    assert "Dhaka Club" in block
    assert INVITE_URL in block


def test_the_block_uses_tables_and_inline_styles_only() -> None:
    """Outlook renders through Word's HTML engine: no flex, no grid. Gmail strips
    `<style>` blocks. Anything else here would look correct in exactly one client."""
    block = _block()
    assert "<table" in block
    assert "<style" not in block
    assert "display:flex" not in block
    assert "display:grid" not in block


def test_customer_typed_values_are_escaped() -> None:
    """A venue name is typed into an admin form, so it reaches here as untrusted text."""
    block = _block(venue="Hotel <script>alert(1)</script> & Suites")
    assert "<script>" not in block
    assert "&lt;script&gt;" in block
    assert "&amp; Suites" in block


def test_a_guest_name_with_markup_in_it_is_escaped() -> None:
    block = _block(guest_name="<b>Rahim</b>")
    assert "<b>Rahim</b>" not in block
    assert "&lt;b&gt;Rahim&lt;/b&gt;" in block


# ------------------------------------------------------------------ how it joins the body


def test_the_block_is_placed_above_the_body() -> None:
    html = templating.to_html("Please let us know if you can come.", preview_block=_block())
    assert html.index("Marriage Ceremony") < html.index("Please let us know")


def test_the_block_survives_the_escape_pass_that_the_body_goes_through() -> None:
    """The whole reason it is a separate parameter: `to_html` escapes its text input, so a
    block concatenated into that would reach the guest as visible angle brackets."""
    html = templating.to_html("Hello", preview_block=_block())
    assert "<table" in html
    assert "&lt;table" not in html


def test_a_body_containing_markup_is_still_escaped() -> None:
    """The counterweight — adding the block must not make the body trusted."""
    html = templating.to_html("2 < 3 & <b>bold</b>", preview_block=_block())
    assert "<b>bold</b>" not in html
    assert "&lt;b&gt;bold&lt;/b&gt;" in html


def test_without_a_block_the_output_is_what_it_always_was() -> None:
    """Reminders and confirmations take this path and must be unchanged."""
    assert templating.to_html("Hello") == templating.to_html("Hello", preview_block="")


def test_the_plain_text_alternative_never_carries_markup() -> None:
    """The block is HTML-part only. The text part is what it was, and the invitation link is
    already in it."""
    body = f"You are invited.\n{INVITE_URL}"
    html = templating.to_html(body, preview_block=_block())
    assert "<table" in html
    assert "<table" not in body
    assert INVITE_URL in body


def test_link_length_checking_still_reads_the_same_string() -> None:
    """`check_link_lengths` runs against the text body, which the block does not touch."""
    body = f"You are invited.\n{INVITE_URL}"
    assert templating.check_link_lengths(body) == templating.check_link_lengths(body)
    assert INVITE_URL in body


# ------------------------------------------------------------------ what must not be stored


@pytest.mark.parametrize("secret", ["Xk3mQ7tokenvalue1234", "Rahim Uddin"])
def test_the_block_carries_exactly_what_must_not_be_stored(secret: str) -> None:
    """Establishes the premise for the test below: the block holds the guest's name and
    their token URL, which is why it is composed at send time and persisted nowhere."""
    assert secret in _block()


def test_the_message_log_exposes_no_message_content() -> None:
    """The block reaches the log only if the log ever grows a content field, so that is what
    this guards. `message_job.subject` and `body_text` do hold hand-edited content by
    design — the rule is that the *log* never surfaces it (CLAUDE.md, design D4).

    A structural assertion rather than a string search: it fails when someone adds the
    field, which is the moment the mistake is cheap to fix.
    """
    from app.routers.admin_messaging import MessageLogEntry

    exposed = set(MessageLogEntry.model_fields)
    assert exposed.isdisjoint({"subject", "body_text", "html", "text", "preview_block"})
