"""`scripts/pr_wave.py` — the title-similarity throttle is not a rate limit.

The behaviour under test is the one that cost a wave a night: Forgejo blocks
"2 similairy named issues in the last hour" — an anti-spam rule on TITLE
SIMILARITY, not a cap on PR creation. The remedy is a different title; sleeping
accomplishes nothing. These tests pin that the wave engine re-titles rather than
backs off, and that it still backs off for the one thing that IS a rate limit.
"""

from __future__ import annotations

import importlib.util
import sys
from pathlib import Path
from typing import Any

import pytest

_SPEC = importlib.util.spec_from_file_location(
    "pr_wave", Path(__file__).resolve().parents[2] / "scripts" / "pr_wave.py"
)
assert _SPEC and _SPEC.loader
pr_wave = importlib.util.module_from_spec(_SPEC)
sys.modules["pr_wave"] = pr_wave
_SPEC.loader.exec_module(pr_wave)

THROTTLE = '"magogi-admin" posted 2 similairy named issues in the last hour: rate limited'
PHRASINGS = ["regenerate the graph", "rebuild generated state", "refresh the projection"]


def test_the_throttle_is_recognised_including_the_upstream_typo() -> None:
    assert pr_wave.is_title_throttle(THROTTLE)
    # a corrected upstream spelling must not silently turn this into a 17-hour wave
    assert pr_wave.is_title_throttle("posted 2 similarly named issues in the last hour")
    assert not pr_wave.is_title_throttle("Branch already exists")


def test_titles_are_distinct_across_the_wave() -> None:
    """Identical titles are the whole bug: 2 land per hour and the rest block."""
    repos = [f"mgf-{i}" for i in range(34)]
    titles = pr_wave.vary_titles(repos, PHRASINGS)
    assert len(set(titles.values())) == len(repos), "a repeated title re-creates the throttle"


def test_first_lap_uses_whole_phrasings_not_a_shared_stem() -> None:
    """The similarity check is FUZZY: a shared phrase with a per-repo prefix still
    clusters, so the first repos must differ in their WORDS."""
    titles = pr_wave.vary_titles(["mgf-a", "mgf-b", "mgf-c"], PHRASINGS)
    assert sorted(titles.values()) == sorted(PHRASINGS)


def test_empty_phrasings_fails_loudly() -> None:
    with pytest.raises(ValueError, match="at least one title phrasing"):
        pr_wave.vary_titles(["mgf-a"], [])


def _poster(script: list[tuple[int, dict[str, Any]]]) -> Any:
    calls: list[dict[str, Any]] = []

    def post(url: str, payload: dict[str, Any], token: str) -> tuple[int, dict[str, Any]]:
        calls.append(payload)
        return script[min(len(calls) - 1, len(script) - 1)]

    post.calls = calls  # type: ignore[attr-defined]
    return post


def test_a_throttled_pr_is_RE_TITLED_and_retried_without_sleeping() -> None:
    """THE POINT. Blocked on title similarity -> change the words, immediately.
    Sleeping here paces for a limit that does not exist."""
    post = _poster([(422, {"message": THROTTLE}), (201, {"number": 7})])
    slept: list[float] = []
    out = pr_wave.run_wave(
        ["mgf-a"], "br", "main", "body", PHRASINGS, "tok", post=post, sleep=slept.append
    )
    assert out == {"mgf-a": 7}
    assert slept == [], "a title throttle must never sleep — the remedy is a different title"
    assert post.calls[0]["title"] != post.calls[1]["title"], "it must actually re-title"


def test_a_real_429_DOES_back_off() -> None:
    """The one case where waiting is the correct remedy — don't throw the baby out."""
    post = _poster([(429, {"message": "too many requests"}), (201, {"number": 9})])
    slept: list[float] = []
    out = pr_wave.run_wave(
        ["mgf-a"], "br", "main", "body", PHRASINGS, "tok", post=post, sleep=slept.append
    )
    assert out == {"mgf-a": 9}
    assert slept, "a genuine 429 must back off"


def test_a_wave_of_many_repos_opens_every_pr() -> None:
    post = _poster([(201, {"number": 1})])
    repos = [f"mgf-{i}" for i in range(34)]
    out = pr_wave.run_wave(repos, "br", "main", "body", PHRASINGS, "tok", post=post)
    assert len(out) == 34
    assert all(isinstance(v, int) for v in out.values())
    assert len({c["title"] for c in post.calls}) == 34


def test_a_non_throttle_error_is_reported_not_retried_forever() -> None:
    post = _poster([(409, {"message": "head branch does not exist"})])
    out = pr_wave.run_wave(["mgf-a"], "br", "main", "body", PHRASINGS, "tok", post=post)
    assert "head branch does not exist" in str(out["mgf-a"])
    assert len(post.calls) == 1, "a real error must not be retried as if it were a throttle"
