"""The deduped-issue pager + OO heartbeat HTTP contract (SPEC-P4 §1), driven with a
fake HttpFn so the create/update/close + fail-open logic is proven without network.
"""

from __future__ import annotations

import json

from mgf.secrets.watch import DEDUPE_QUERY, CodebergIssuePager, OOHeartbeat


class FakeHttp:
    def __init__(self, get_response: tuple[int, str] = (200, "[]")) -> None:
        self.calls: list[tuple[str, str, dict[str, str], str]] = []
        self._get_response = get_response

    def __call__(self, method, url, headers, body):
        self.calls.append((method, url, headers, body.decode() if body else ""))
        if method == "GET":
            return self._get_response
        return 201, "{}"

    def of(self, method: str) -> list[tuple[str, str, dict[str, str], str]]:
        return [c for c in self.calls if c[0] == method]


OPEN_ISSUE = json.dumps(
    [{"number": 7, "title": "[estate] secret-doctor: credential drift detected"}]
)


def test_report_creates_when_no_open_issue() -> None:
    http = FakeHttp(get_response=(200, "[]"))
    pager = CodebergIssuePager(token="tok", http=http)
    assert pager.report(dedupe_query=DEDUPE_QUERY, title="T", body="B") == "filed"
    posts = http.of("POST")
    assert len(posts) == 1 and posts[0][1].endswith("/issues")
    payload = json.loads(posts[0][3])
    assert payload["title"] == "T" and payload["body"] == "B"
    # Forgejo/Gitea token auth (not Bearer), matching the estate contract.
    assert posts[0][2]["Authorization"] == "token tok"


def test_report_updates_when_issue_open_deduped() -> None:
    http = FakeHttp(get_response=(200, OPEN_ISSUE))
    pager = CodebergIssuePager(token="tok", http=http)
    assert pager.report(dedupe_query=DEDUPE_QUERY, title="T", body="B2") == "updated"
    patches = http.of("PATCH")
    assert len(patches) == 1 and patches[0][1].endswith("/issues/7")
    assert json.loads(patches[0][3]) == {"body": "B2"}
    assert not http.of("POST")  # no duplicate issue


def test_resolve_closes_open_issue_with_a_comment() -> None:
    http = FakeHttp(get_response=(200, OPEN_ISSUE))
    pager = CodebergIssuePager(token="tok", http=http)
    assert pager.resolve(dedupe_query=DEDUPE_QUERY, comment="clean") == "closed"
    assert http.of("POST")[0][1].endswith("/issues/7/comments")
    assert json.loads(http.of("PATCH")[0][3]) == {"state": "closed"}


def test_resolve_noop_when_nothing_open() -> None:
    http = FakeHttp(get_response=(200, "[]"))
    pager = CodebergIssuePager(token="tok", http=http)
    assert pager.resolve(dedupe_query=DEDUPE_QUERY, comment="clean") == "none"
    assert not http.of("PATCH") and not http.of("POST")


def test_heartbeat_payload_shape_and_success() -> None:
    http = FakeHttp()

    def hb_http(method, url, headers, body):
        http.calls.append((method, url, headers, body.decode() if body else ""))
        return 200, ""

    hb = OOHeartbeat(
        url="https://oo/api/default/backup_heartbeats/_json", token="hbtok", http=hb_http
    )
    assert hb.beat("secret-doctor") is True
    method, url, headers, body = http.calls[0]
    assert method == "POST" and url.endswith("/_json")
    assert headers["Authorization"] == "hbtok"  # verbatim token, no prefix added
    payload = json.loads(body)
    assert payload[0]["job"] == "secret-doctor" and payload[0]["status"] == "ok"
    assert "host" in payload[0] and "ts" in payload[0]


def test_heartbeat_is_fail_open_on_error_status_and_exception() -> None:
    hb_bad = OOHeartbeat(url="https://oo/x", token="t", http=lambda *a: (500, "err"))
    assert hb_bad.beat("secret-doctor") is False  # non-2xx -> False, never raises

    def boom(*_a):
        raise OSError("connection refused")

    hb_boom = OOHeartbeat(url="https://oo/x", token="t", http=boom)
    assert hb_boom.beat("secret-doctor") is False  # OSError swallowed (fail-open)
