"""The outbox, and the promise it is allowed to make.

Holding a registration locally is a weakening of "nothing exists only inside
Python", approved on the understanding that it is bounded, visible, and never
lets a document advance while it is held. These tests are what make those three
words mean something — without them the outbox is just the hidden queue again,
renamed.
"""

from __future__ import annotations

from datetime import UTC, datetime, timedelta

import pytest

from app.intake import InMemoryOutbox, OutboxFull, PendingRegistration


def arrival(message_id: str = "MSG-1", **overrides) -> PendingRegistration:
    fields = {
        "source": "whatsapp",
        "source_account": "923049637232",
        "provider_message_id": message_id,
        "original_filename": "CamScanner 07-06-2026 09.33.pdf",
        "mime_type": "application/pdf",
        "size_bytes": 182_301,
        "path": "incoming/MSG-1.pdf",
        "received_at": datetime.now(UTC),
    }

    return PendingRegistration(**(fields | overrides))


def test_a_held_registration_keeps_everything_needed_to_send_it_later():
    outbox = InMemoryOutbox()
    outbox.add(arrival())

    body = outbox.oldest().submission()

    assert body["source"] == "whatsapp"
    assert body["provider_message_id"] == "MSG-1"
    assert body["original_filename"] == "CamScanner 07-06-2026 09.33.pdf"

    # The file itself is not held in the row — it is on disk, and re-read at send
    # time. Holding base64 of every queued document in the database would turn a
    # CMS outage into a disk problem.
    assert "file_base64" not in body


def test_the_same_message_is_not_queued_twice():
    outbox = InMemoryOutbox()
    outbox.add(arrival())
    outbox.add(arrival())

    assert len(outbox) == 1


def test_sources_without_a_message_id_do_not_collide():
    outbox = InMemoryOutbox()
    outbox.add(arrival(message_id=None, source="manual"))
    outbox.add(arrival(message_id=None, source="manual"))

    # A manual upload carries no provider id. Treating two of them as the same
    # document would silently drop the second.
    assert len(outbox) == 2


def test_it_refuses_rather_than_growing_without_limit():
    outbox = InMemoryOutbox(capacity=3)

    for index in range(3):
        outbox.add(arrival(message_id=f"MSG-{index}"))

    # The cap is the honest part of the bargain. An outbox that accepts forever
    # hides the outage it exists to survive.
    with pytest.raises(OutboxFull):
        outbox.add(arrival(message_id="MSG-overflow"))

    assert len(outbox) == 3


def test_a_registration_is_only_forgotten_once_the_cms_has_it():
    outbox = InMemoryOutbox()
    outbox.add(arrival())

    held = outbox.oldest()
    outbox.failed(held, "connection refused")

    # Still held. A failed attempt must not look like a delivered one.
    assert len(outbox) == 1
    assert outbox.oldest().attempts == 1
    assert outbox.oldest().last_error == "connection refused"

    outbox.done(outbox.oldest())
    assert len(outbox) == 0


def test_the_oldest_goes_first():
    outbox = InMemoryOutbox()
    now = datetime.now(UTC)

    outbox.add(arrival(message_id="NEW", received_at=now))
    outbox.add(arrival(message_id="OLD", received_at=now - timedelta(hours=2)))

    # By arrival, not by when it happened to be queued: the document somebody is
    # asking about is the one that has been waiting two hours, even though it was
    # added second. Both stores must agree on this — the Postgres one orders by
    # received_at, so the in-memory one cannot answer in insertion order.
    assert outbox.oldest().provider_message_id == "OLD"
    assert [item.provider_message_id for item in outbox.all()] == ["OLD", "NEW"]


def test_waiting_is_measured_from_arrival_not_from_the_last_attempt():
    received = datetime.now(UTC) - timedelta(minutes=30)
    held = arrival(received_at=received, attempts=6)

    # Retrying does not reset the clock. "How long has the client been waiting"
    # is the question this answers, and it is not "how long since we last tried".
    assert held.waited() == pytest.approx(1800, abs=5)


def test_a_refused_document_can_be_queued_with_no_file_at_all():
    outbox = InMemoryOutbox()
    outbox.add(
        PendingRegistration(
            source="whatsapp",
            provider_message_id="REFUSED-1",
            original_filename="contract.docx",
            failure_reason="Unsupported file type.",
            received_at=datetime.now(UTC),
        )
    )

    body = outbox.oldest().submission()

    # The arrival still has to reach the CMS even though the agent refused the
    # document — otherwise a client sends something and nothing anywhere records
    # that they did.
    assert body["failure_reason"] == "Unsupported file type."
    assert outbox.oldest().path is None
