from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[2]
spec = importlib.util.spec_from_file_location("theme_safe_merge", ROOT / "tools" / "theme_safe_merge.py")
sm = importlib.util.module_from_spec(spec)
assert spec.loader
sys.modules[spec.name] = sm
spec.loader.exec_module(sm)

SHA = "a" * 40


def fake_pr(*, draft: bool = False, base: str = "main", sha: str = SHA):
    return {
        "number": 7,
        "state": "OPEN",
        "isDraft": draft,
        "baseRefName": base,
        "headRefOid": sha,
        "mergeable": "MERGEABLE",
        "mergeStateStatus": "CLEAN",
        "statusCheckRollup": [{
            "name": "Full gate",
            "workflowName": "ThemeFactory Verify",
            "status": "COMPLETED",
            "conclusion": "SUCCESS",
        }],
        "url": "https://example.invalid/pr/7",
    }


def fake_checks(*, conclusion: str = "success", sha: str = SHA, app: str = "github-actions"):
    return {
        "check_runs": [{
            "id": 42,
            "name": "Full gate",
            "head_sha": sha,
            "status": "completed",
            "conclusion": conclusion,
            "completed_at": "2026-08-23T00:00:00Z",
            "details_url": "https://github.com/example/repo/actions/runs/1/job/2",
            "app": {"slug": app},
        }]
    }


def test_safe_merge_validates_exact_sha_github_actions_full_gate(monkeypatch):
    def json_cmd(args):
        return fake_pr() if args[:3] == ["gh", "pr", "view"] else fake_checks()
    monkeypatch.setattr(sm, "json_cmd", json_cmd)
    monkeypatch.setattr(sm, "local_head", lambda: SHA)
    evidence = sm.validate("example/repo", 7)
    assert evidence["result"] == "PASS"
    assert evidence["head_sha"] == SHA
    assert evidence["check_run_id"] == 42


def test_safe_merge_rejects_draft(monkeypatch):
    monkeypatch.setattr(sm, "json_cmd", lambda args: fake_pr(draft=True))
    monkeypatch.setattr(sm, "local_head", lambda: SHA)
    with pytest.raises(sm.MergeRefused, match="still draft"):
        sm.inspect_pr("example/repo", 7)


def test_safe_merge_rejects_local_head_mismatch(monkeypatch):
    monkeypatch.setattr(sm, "json_cmd", lambda args: fake_pr())
    monkeypatch.setattr(sm, "local_head", lambda: "b" * 40)
    with pytest.raises(sm.MergeRefused, match="local HEAD does not match"):
        sm.inspect_pr("example/repo", 7)


def test_safe_merge_rejects_non_success_or_non_actions_check(monkeypatch):
    monkeypatch.setattr(sm, "json_cmd", lambda args: fake_checks(conclusion="failure"))
    with pytest.raises(sm.MergeRefused, match="success not found"):
        sm.exact_sha_full_gate("example/repo", SHA)
    monkeypatch.setattr(sm, "json_cmd", lambda args: fake_checks(app="other-app"))
    with pytest.raises(sm.MergeRefused, match="success not found"):
        sm.exact_sha_full_gate("example/repo", SHA)


def test_safe_merge_uses_match_head_commit_and_never_admin(monkeypatch):
    monkeypatch.setattr(sm, "validate", lambda repo, pr: {"repo": repo, "pr": pr, "head_sha": SHA, "result": "PASS"})
    calls = []
    class CP:
        returncode = 0
        stdout = ""
        stderr = ""
    monkeypatch.setattr(sm, "run", lambda args, check=True: (calls.append(args) or CP()))
    result = sm.merge("example/repo", 7)
    assert result["merged"] is True
    assert calls == [["gh", "pr", "merge", "7", "--repo", "example/repo", "--merge", "--match-head-commit", SHA]]
    assert "--admin" not in calls[0]
    assert "--auto" not in calls[0]
