"""PR-* test plan T9 — pin the canonical output strings.

The fmt helpers' output is part of the SDK's PUBLIC API surface
for PR-06 dog-fooding: hrb-app reads the operator-visible strings
back through ``DoctorRow.severity_glyph`` (and equivalents). Any
silent change to the canonical band shape (``"  OK "`` →
``"[OK]"``, say) would silently re-render every operator's UI.

This file pins the exact canonical outputs as a wire-format
contract. Changes here must be deliberate and ship with a
CHANGELOG entry — same discipline as the wire-protocol pins in
``tests/contract/``.
"""

from __future__ import annotations

import pytest

from mgf.hrb.fmt import (
    format_duration,
    format_rss,
    format_severity_glyph,
    format_uptime,
)

pytestmark = pytest.mark.contract


# ---------------------------------------------------------------------------
# Severity glyphs — 5-char bands, exact match (public-API stable)
# ---------------------------------------------------------------------------


def test_severity_glyph_canonical_bands() -> None:
    """Public-API contract — operators rely on the 5-char width
    for column alignment. Width MUST be 5; characters MUST match
    these exact strings."""
    assert format_severity_glyph("ok") == "  OK "
    assert format_severity_glyph("warn") == " WARN"
    assert format_severity_glyph("fail") == " FAIL"
    assert format_severity_glyph("deferred") == " ... "
    # Defensive unknown-glyph — also part of the contract.
    assert format_severity_glyph("garbage") == "  ?  "


def test_severity_glyph_width_is_uniform() -> None:
    """Width invariant — column-alignment in CLI / Qt depends on
    every glyph being exactly 5 chars wide. If a new severity
    glyph is added later, this test catches the contract drift."""
    for severity in ("ok", "warn", "fail", "deferred", "unknown_x"):
        assert len(format_severity_glyph(severity)) == 5, (
            f"glyph for {severity!r} is not 5 chars: {format_severity_glyph(severity)!r}"
        )


# ---------------------------------------------------------------------------
# format_uptime — boundary values pinning the band-cascade
# ---------------------------------------------------------------------------


def test_uptime_canonical_boundary_values() -> None:
    """Pin the exact strings at every band boundary. Catches both
    silent reformatting and off-by-one errors at the seconds /
    minutes / hours transitions."""
    # Sub-second clamps to "0s".
    assert format_uptime(0) == "0s"
    assert format_uptime(500) == "0s"
    assert format_uptime(999) == "0s"

    # 1s..59s: no minutes.
    assert format_uptime(1_000) == "1s"
    assert format_uptime(59_000) == "59s"

    # 1m..59m: "Xm Ys".
    assert format_uptime(60_000) == "1m 0s"
    assert format_uptime(61_000) == "1m 1s"
    assert format_uptime(3_599_000) == "59m 59s"

    # ≥1h: "Xh Ym Zs".
    assert format_uptime(3_600_000) == "1h 0m 0s"
    assert format_uptime(3_661_000) == "1h 1m 1s"
    assert format_uptime(86_400_000) == "24h 0m 0s"


# ---------------------------------------------------------------------------
# format_duration — boundary values pinning the unit cascade
# ---------------------------------------------------------------------------


def test_duration_canonical_boundary_values() -> None:
    """µs → ms → s → m+s. Pin each band boundary."""
    # µs band — bare integer + "µs"
    assert format_duration(0) == "0µs"
    assert format_duration(1) == "1µs"
    assert format_duration(999) == "999µs"

    # ms band — one decimal place
    assert format_duration(1_000) == "1.0ms"
    assert format_duration(4_500) == "4.5ms"
    assert format_duration(999_000) == "999.0ms"

    # s band
    assert format_duration(1_000_000) == "1.0s"
    assert format_duration(59_500_000) == "59.5s"

    # m+s band — whole seconds, no decimals
    assert format_duration(60_000_000) == "1m0s"
    assert format_duration(312_000_000) == "5m12s"


# ---------------------------------------------------------------------------
# format_rss — binary prefixes (IEC), NOT decimal (SI)
# ---------------------------------------------------------------------------


def test_rss_uses_binary_prefixes() -> None:
    """1024-based prefixes (KiB / MiB / GiB / TiB / PiB). NOT 1000-
    based (kB / MB). Linux reports memory in binary; we match that
    to prevent the classic '42 MiB on screen, 44 MB on free -h'
    operator confusion."""
    assert format_rss(0) == "0 B"
    assert format_rss(1) == "1 B"
    assert format_rss(1023) == "1023 B"

    # KiB band — 1024 == 1.0 KiB (binary)
    assert format_rss(1024) == "1.0 KiB"
    assert format_rss(1_500) == "1.5 KiB"

    # MiB band — common operator value
    assert format_rss(44_400_000) == "42.3 MiB"
    # 1_000_000 in binary is ~976.5 KiB, NOT 1.0 MB
    assert "MB" not in format_rss(1_000_000)
    assert format_rss(1_000_000) == "976.6 KiB"

    # GiB band
    assert format_rss(1_500_000_000) == "1.4 GiB"
