"""The AG-03 commit-trailer CI gate — predicate AND range shell, without git.

This file is **distributed byte-identically** to every repo carrying the gate, so
everything in it must lint clean under the strictest config in the fleet: 34 of 54
repos select ruff's bandit `S` rules and `mgf-standard` does not, which means a
file containing `subprocess` cannot exist here (the suppressing `noqa` those 34
need is an *unused* noqa here, and `RUF100` rejects it). It therefore drives the
gate through the checker's own seams — `_run` and `_ref_exists` — with a fake.

That is a constraint, not an excuse, and it was nearly a hole: an earlier draft's
fake dispatched on the *verb* alone and ignored every ref and sha, so a checker
aimed at `HEAD~1` instead of `HEAD` — the PAPER-162 class verbatim — passed it.
A fresh-context chair caught that by execution. The fake below keys on the exact
argv and answers *"both trailers present"* for any commit that is not the tip, so
checking the wrong commit fails loudly rather than silently.

An earlier version of this docstring said the git plumbing was "a thin shell
exercised in CI, not here". mgf-common PAPER-162 is what that assumption cost:
CI ran the gate green for months while it checked NOTHING, because a vacuous pass
and a real pass print the same thing and exit the same code. **CI cannot detect
its own vacuous pass** — that is the one thing it is structurally unable to do.

The real-git proof lives at canon in `test_check_commit_trailers_realgit.py`,
which is deliberately NOT distributed for the lint reason above.
"""

from __future__ import annotations

import importlib.util
from pathlib import Path

import pytest

# scripts/ is not a package; load the module by path.
_SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "check_commit_trailers.py"
_spec = importlib.util.spec_from_file_location("check_commit_trailers", _SCRIPT)
assert _spec and _spec.loader
cct = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(cct)

_BOTH = (
    "Signed-off-by: Bassam Alsanie <bassam@magogi.org>\n"
    "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"
)

_TIP = "a" * 40  # HEAD's own commit — the one a missing trailer would be on
_OTHER = "b" * 40  # anything else the checker might reach for instead


class TestMissingTrailers:
    def test_both_present_returns_empty(self) -> None:
        assert cct.missing_trailers(_BOTH) == []

    def test_missing_signoff(self) -> None:
        assert cct.missing_trailers("Co-Authored-By: X <y@z>") == ["signed-off-by"]

    def test_missing_coauthor(self) -> None:
        assert cct.missing_trailers("Signed-off-by: B <b@m>") == ["co-authored-by"]

    def test_both_missing(self) -> None:
        assert cct.missing_trailers("") == ["signed-off-by", "co-authored-by"]

    def test_key_match_is_case_insensitive(self) -> None:
        # git treats trailer keys case-insensitively; `Co-authored-by` counts.
        block = "Signed-Off-By: B <b@m>\nCo-authored-by: X <y@z>"
        assert cct.missing_trailers(block) == []

    def test_extra_trailers_and_blank_lines_ignored(self) -> None:
        block = f"\nReviewed-by: Q <q@r>\n{_BOTH}\n"
        assert cct.missing_trailers(block) == []

    @pytest.mark.parametrize("noise", ["", "   ", "not a trailer line", "no-colon-here"])
    def test_non_trailer_lines_do_not_satisfy(self, noise: str) -> None:
        # A line without a recognizable `key:` must not count as a trailer.
        assert set(cct.missing_trailers(noise)) == {"signed-off-by", "co-authored-by"}


# --- the range shell, which is where the defect actually was --------------------


class _FakeGit:
    """Canned `git` answers keyed on the EXACT argv, not on a verb.

    Two rules make this a test rather than a tautology:

    * an argv the checker was not expected to issue raises, so a silent change of
      plumbing is loud here;
    * **any commit other than the tip answers with BOTH trailers present.** So a
      checker that inspects the wrong commit — `HEAD~1`, the base, anything —
      reports success, and the test that demands failure reds. Verified: a chair
      changed `-1 HEAD` to `-1 HEAD~1` in the checker and an earlier
      verb-dispatching fake stayed entirely green.
    """

    def __init__(self, *, range_shas: list[str], tip_trailers: str) -> None:
        self.range_shas = range_shas
        self.tip_trailers = tip_trailers
        self.asked: list[list[str]] = []

    def __call__(self, args: list[str]) -> str:
        self.asked.append(list(args[1:]))  # drop the git binary path
        tail = args[1:]
        if tail[:2] == ["symbolic-ref", "--quiet"]:
            return "refs/remotes/origin/main\n"
        if tail[:1] == ["log"] and "--format=%H" in tail:
            return "\n".join(self.range_shas)
        if tail[:1] == ["rev-list"]:
            # The checker asks for ONE ref here. Only HEAD is the commit being
            # pushed; anything else is the defect this fixture exists to catch.
            return _TIP if tail[-1] == "HEAD" else _OTHER
        if tail[:1] == ["log"] and any(a.startswith("--format=%(trailers") for a in tail):
            return self.tip_trailers if tail[-1] == _TIP else _BOTH
        raise AssertionError(f"the checker issued an argv this fixture does not model: {tail}")


@pytest.fixture
def seam(monkeypatch: pytest.MonkeyPatch):  # type: ignore[no-untyped-def]
    """`main()` with git replaced — no subprocess, no repo, no network.

    `_resolve_base` is NOT faked away: it runs for real over a faked `_ref_exists`,
    so `_base_candidates`, `origin/HEAD` resolution and the CI provenance rule are
    all exercised in every repo that receives this file. An earlier draft stubbed
    `_resolve_base` out, and a chair proved the cost — reverting base resolution to
    the pre-fix `return "main"` left this file entirely green.
    """

    def _install(
        *, range_shas: list[str], tip_trailers: str, refs: set[str] | None = None, ci: bool = True
    ) -> _FakeGit:
        fake = _FakeGit(range_shas=range_shas, tip_trailers=tip_trailers)
        existing = {"origin/main"} if refs is None else refs
        monkeypatch.setattr(cct, "_require_git", lambda: None)
        monkeypatch.setattr(cct, "_run", fake)
        monkeypatch.setattr(cct, "_ref_exists", lambda ref: ref in existing)
        monkeypatch.setenv("CI", "1") if ci else monkeypatch.delenv("CI", raising=False)
        monkeypatch.delenv("CI_COMMIT_TARGET_BRANCH", raising=False)
        return fake

    return _install


def test_empty_range_checks_head_rather_than_passing_vacuously(seam) -> None:  # type: ignore[no-untyped-def]
    """mgf-common PAPER-162, the live half.

    On a push to the default branch — the fleet's most common CI event —
    `origin/<default>` IS the commit just pushed, so `base..HEAD` is empty by
    construction. The gate printed "all 0 commit(s) on this branch carry both
    trailers" and exited 0 over a commit with no trailers at all.
    """
    fake = seam(range_shas=[], tip_trailers="")
    assert cct.main() != 0, "an empty range passed vacuously over an untrailered commit"
    assert ["rev-list", "--no-merges", "-1", "HEAD"] in fake.asked, (
        "the empty-range branch must ask about HEAD itself — asking about any other "
        "commit is the same defect wearing a different ref"
    )


def test_empty_range_still_passes_a_properly_trailered_head(seam) -> None:  # type: ignore[no-untyped-def]
    """The counterpart: closing the fail-open must not red every push."""
    seam(range_shas=[], tip_trailers=_BOTH)
    assert cct.main() == 0


def test_non_empty_range_is_unaffected(seam) -> None:  # type: ignore[no-untyped-def]
    """The ordinary PR shape must keep working — the fix is scoped to empty."""
    seam(range_shas=[_TIP], tip_trailers="")
    assert cct.main() != 0
    seam(range_shas=[_TIP], tip_trailers=_BOTH)
    assert cct.main() == 0


def test_in_ci_the_local_branch_name_is_never_trusted(seam) -> None:  # type: ignore[no-untyped-def]
    """AG-03's flagship defence, which had no seeded self-test anywhere.

    On a `pull_request` the runner checks the PR head out UNDER THE DEFAULT
    BRANCH'S NAME, so a bare `main` is counterfeit — it is the branch under
    review. `_resolve_base` therefore consults only `origin/<branch>` when `CI` is
    set. A chair deleted that guard and the whole suite stayed green.

    Here `origin/main` does not exist and a bare `main` does. The tell is not the
    exit code — the checker deliberately falls back to inspecting HEAD's own commit
    when no base resolves, so both runs below exit 0 — it is **whether a range was
    computed at all**. Under CI the counterfeit `main` must not become a base;
    outside CI the same ref is legitimate, because a developer running this by hand
    has a real `main`. Same input, opposite handling, decided by provenance.
    """

    def computed_a_range(fake: _FakeGit) -> bool:
        return any("--format=%H" in call for call in fake.asked)

    in_ci = seam(range_shas=[], tip_trailers=_BOTH, refs={"main"}, ci=True)
    assert cct.main() == 0
    assert not computed_a_range(in_ci), (
        "under CI a bare `main` is the branch under review, not the base — "
        "computing a range against it compares the PR head with itself"
    )

    off_ci = seam(range_shas=[], tip_trailers=_BOTH, refs={"main"}, ci=False)
    assert cct.main() == 0
    assert computed_a_range(off_ci), (
        "outside CI the local `main` IS the base; refusing it would make the "
        "checker unusable by hand, which is how a gate earns a blanket waiver"
    )
