"""Reading this process's own memory, and never failing loudly while doing it."""

from __future__ import annotations

import builtins

import pytest

from app.runtime import process_memory


@pytest.fixture(autouse=True)
def _forget_peak():
    """Each test starts with no high-water mark inherited from the last."""
    process_memory.reset_peak()
    yield
    process_memory.reset_peak()


class TestWhatItReports:
    def test_it_reports_a_plausible_size_for_this_process(self):
        usage = process_memory.usage()

        assert usage["rss_mb"] > 0, "a running Python process holds more than nothing"
        assert usage["rss_mb"] < 100_000, "and less than 100 GB"

    def test_the_peak_is_never_below_the_current_reading(self):
        usage = process_memory.usage()

        assert usage["peak_rss_mb"] >= usage["rss_mb"]

    def test_the_peak_remembers_a_reading_the_current_figure_has_moved_past(self, monkeypatch):
        monkeypatch.setattr(process_memory, "_rss_bytes", lambda: 900 * 1024 * 1024)
        assert process_memory.usage() == {"rss_mb": 900, "peak_rss_mb": 900}

        # Fell back. The point of the peak is that this does not erase it: one
        # sample cannot tell a settled process from one on its way up.
        monkeypatch.setattr(process_memory, "_rss_bytes", lambda: 400 * 1024 * 1024)
        assert process_memory.usage() == {"rss_mb": 400, "peak_rss_mb": 900}


class TestItNeverBreaksTheStatusReport:
    def test_an_absent_psutil_costs_the_figure_and_nothing_else(self, monkeypatch):
        """A deployment that pulled new code without reinstalling dependencies.

        It must lose the memory reading, not its whole status report — which is
        why the import is inside the function rather than at module scope.
        """
        real_import = builtins.__import__

        def refuse_psutil(name, *args, **kwargs):
            if name == "psutil":
                raise ImportError("No module named 'psutil'")

            return real_import(name, *args, **kwargs)

        monkeypatch.setattr(builtins, "__import__", refuse_psutil)

        assert process_memory.usage() == {}

    def test_a_reader_that_raises_is_swallowed(self, monkeypatch):
        def explode():
            raise OSError("the kernel declined to say")

        monkeypatch.setattr(process_memory, "_rss_bytes", explode)

        with pytest.raises(OSError):
            process_memory._rss_bytes()

        # But usage() is what the status report calls, and it must not raise.
        monkeypatch.setattr(process_memory, "_rss_bytes", lambda: None)
        assert process_memory.usage() == {}


class TestInTheStatusReport:
    def test_the_report_carries_the_process_block(self):
        from app.runtime import status_report

        report = status_report.build(_StubContainer())

        assert report["process"]["rss_mb"] > 0
        assert "peak_rss_mb" in report["process"]

    def test_the_block_is_omitted_rather_than_zeroed_when_unreadable(self, monkeypatch):
        """A page saying "0 MB" would describe a deployment using no memory at
        all, which is a stranger claim than saying nothing."""
        from app.runtime import status_report

        monkeypatch.setattr(process_memory, "_rss_bytes", lambda: None)

        assert "process" not in status_report.build(_StubContainer())


class _StubContainer:
    """Enough container for build() to assemble a report. Everything it reaches
    for is optional, and build() is documented never to raise."""

    connect = None
    ocr = None
    whatsapp = None
    inbox = None
