"""Copying an invitation link is an audited read, not a send (task 8.7, design D8).

The whole point of the endpoint is what it does *not* do. An admin handing a link to a guest
over WhatsApp has not delivered an email, and a host looking at their message history later
must not find one there — so these tests mostly assert absence.

The one positive assertion is the audit entry, which answers "who gave this link out, and
when" after the fact.
"""

import uuid
from typing import Any

import pytest
from httpx import ASGITransport, AsyncClient
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.main import app
from app.services import audit

from .conftest import requires_db

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


async def _job_count(session: AsyncSession, invitation_id: uuid.UUID) -> int:
    count = await session.scalar(
        text("SELECT count(*) FROM message_job WHERE invitation_id = :i"), {"i": invitation_id}
    )
    return int(count or 0)


async def _audit_rows(session: AsyncSession, invitation_id: uuid.UUID) -> list[Any]:
    rows = await session.execute(
        text(
            "SELECT action, actor_email, after FROM audit_log WHERE entity_id = :i AND action = :a"
        ),
        {"i": invitation_id, "a": audit.Actions.INVITATION_LINK_COPY},
    )
    return list(rows)


async def test_copying_records_an_audit_entry(
    session: AsyncSession, fixture_event: dict[str, Any]
) -> None:
    invitation_id = fixture_event["invitation_ids"][0]

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post(f"/api/admin/invitations/{invitation_id}/link-copied")

    assert response.status_code == 204, response.text
    rows = await _audit_rows(session, invitation_id)
    assert len(rows) == 1
    assert rows[0][0] == "invitation.link_copy"
    assert rows[0][1]  # the admin who took it


async def test_copying_queues_nothing_and_sends_nothing(
    session: AsyncSession, fixture_event: dict[str, Any]
) -> None:
    """A copy that produced a `message_job` would show up in the host's history as a
    delivery that never happened."""
    invitation_id = fixture_event["invitation_ids"][0]
    before = await _job_count(session, invitation_id)

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        await client.post(f"/api/admin/invitations/{invitation_id}/link-copied")

    assert await _job_count(session, invitation_id) == before


async def test_copying_does_not_move_the_invitation_or_its_open_count(
    session: AsyncSession, fixture_event: dict[str, Any]
) -> None:
    invitation_id = fixture_event["invitation_ids"][0]
    row = (
        await session.execute(
            text("SELECT status, open_count, opened_at FROM invitation WHERE id = :i"),
            {"i": invitation_id},
        )
    ).one()

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        await client.post(f"/api/admin/invitations/{invitation_id}/link-copied")

    session.expire_all()
    after = (
        await session.execute(
            text("SELECT status, open_count, opened_at FROM invitation WHERE id = :i"),
            {"i": invitation_id},
        )
    ).one()
    assert tuple(after) == tuple(row)


async def test_a_suppressed_guests_link_can_still_be_copied(
    session: AsyncSession, fixture_event: dict[str, Any]
) -> None:
    """Suppression governs what the system sends, not what a host may hand someone in
    person. The panel states the suppression above the control, so the admin chooses
    knowingly — and the audit entry records that they did."""
    invitation_id = fixture_event["invitation_ids"][0]
    await session.execute(
        text(
            "UPDATE guest SET do_not_contact = true WHERE id = "
            "(SELECT guest_id FROM invitation WHERE id = :i)"
        ),
        {"i": invitation_id},
    )
    await session.commit()

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post(f"/api/admin/invitations/{invitation_id}/link-copied")

    assert response.status_code == 204, response.text
    rows = await _audit_rows(session, invitation_id)
    assert rows[0][2]["suppressed"] is True


async def test_the_audit_entry_carries_no_token(
    session: AsyncSession, fixture_event: dict[str, Any]
) -> None:
    """The log is read by people and is not a place a bearer secret belongs (design D11)."""
    invitation_id = fixture_event["invitation_ids"][0]
    token = await session.scalar(
        text("SELECT token FROM invitation WHERE id = :i"), {"i": invitation_id}
    )

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        await client.post(f"/api/admin/invitations/{invitation_id}/link-copied")

    rows = await _audit_rows(session, invitation_id)
    assert str(token) not in str(rows[0][2])


async def test_copying_an_unknown_invitation_is_not_found() -> None:
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post(f"/api/admin/invitations/{uuid.uuid4()}/link-copied")
    assert response.status_code == 404


async def test_the_compose_panel_carries_the_preview(
    session: AsyncSession, fixture_event: dict[str, Any]
) -> None:
    """What the admin approves has to be what the guest receives, so the panel is served the
    same preview the email block is built from."""
    invitation_id = fixture_event["invitation_ids"][0]

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.get(f"/api/admin/invitations/{invitation_id}/message")

    assert response.status_code == 200, response.text
    preview = response.json()["preview"]
    # The fixture publishes no card design, so this is also the degradation path.
    assert preview["has_image"] is False
    assert preview["image_url"] is None
    assert preview["guest_name"] == "Guest 0"
    assert "Walima" in preview["title"]
