"""ORCH-022 / TC-13..TC-15: the security-gate QG wrapper + registry wiring.

Covers the thin ``check_security_gate`` registry wrapper in src/qg/checks.py (its
conditionality fast-paths) and that the new check is registered + dispatched by
``_run_qg``. The deterministic core (scan / verdict / frontmatter) is covered in
tests/test_security_gate.py.
"""

import os

os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")

from src import security_gate as sg  # noqa: E402
from src.qg import checks as qg  # noqa: E402
from src.qg.checks import QG_CHECKS, check_security_gate  # noqa: E402

_WI = "ORCH-022"
_BRANCH = "feature/ORCH-022-x"


# ---------------------------------------------------------------------------
# TC-13 — non-self repo with empty scope -> N/A fast pass (no scanner run).
# ---------------------------------------------------------------------------
def test_tc13_non_self_repo_empty_scope_is_na(monkeypatch):
    """TC-13: a non-self repo with an empty scope -> (True, 'security-gate N/A
    for <repo>') immediately, WITHOUT invoking the scanners."""
    monkeypatch.setattr(sg.settings, "security_gate_enabled", True)
    monkeypatch.setattr(sg.settings, "security_gate_repos", "")

    called = {"scan": False}

    def _should_not_run(*a, **k):
        called["scan"] = True
        raise AssertionError("scanner must not run for an N/A repo")

    monkeypatch.setattr(sg, "scan_secrets", _should_not_run)
    monkeypatch.setattr(sg, "audit_dependencies", _should_not_run)

    ok, reason = check_security_gate("enduro-trails", _WI, _BRANCH)
    assert ok is True
    assert "N/A" in reason
    assert "enduro-trails" in reason
    assert called["scan"] is False


# ---------------------------------------------------------------------------
# TC-14 — kill-switch disabled -> no-op pass.
# ---------------------------------------------------------------------------
def test_tc14_disabled_is_noop_pass(monkeypatch):
    """TC-14: ORCH_SECURITY_GATE_ENABLED=false -> no-op pass (True), scanners untouched."""
    monkeypatch.setattr(sg.settings, "security_gate_enabled", False)

    def _should_not_run(*a, **k):
        raise AssertionError("scanner must not run when the gate is disabled")

    monkeypatch.setattr(sg, "scan_secrets", _should_not_run)
    monkeypatch.setattr(sg, "audit_dependencies", _should_not_run)

    ok, reason = check_security_gate("orchestrator", _WI, _BRANCH)
    assert ok is True
    assert "disabled" in reason.lower()


# ---------------------------------------------------------------------------
# TC-15 — registered in QG_CHECKS + dispatched by _run_qg.
# ---------------------------------------------------------------------------
def test_tc15_registered_in_qg_checks():
    """TC-15a: the new check is registered and callable."""
    assert "check_security_gate" in QG_CHECKS
    assert QG_CHECKS["check_security_gate"] is check_security_gate
    assert callable(QG_CHECKS["check_security_gate"])


def test_tc15_dispatched_by_run_qg(monkeypatch):
    """TC-15b: _run_qg routes 'check_security_gate' with the (repo, work_item_id,
    branch) signature to the registered wrapper."""
    from src import stage_engine

    captured = {}

    def _fake(repo, work_item_id, branch):
        captured["args"] = (repo, work_item_id, branch)
        return True, "ok"

    monkeypatch.setitem(stage_engine.QG_CHECKS, "check_security_gate", _fake)
    passed, reason = stage_engine._run_qg("check_security_gate", "orchestrator", _WI, _BRANCH)
    assert passed is True
    assert captured["args"] == ("orchestrator", _WI, _BRANCH)


def test_security_gate_applies_scope(monkeypatch):
    """Conditionality matrix mirrors merge_gate_applies / image_freshness_applies."""
    monkeypatch.setattr(sg.settings, "security_gate_enabled", True)
    # Empty scope -> only the self-hosting repo.
    monkeypatch.setattr(sg.settings, "security_gate_repos", "")
    assert sg.security_gate_applies("orchestrator") is True
    assert sg.security_gate_applies("enduro-trails") is False
    # Explicit CSV scope -> only the listed repos (case-insensitive).
    monkeypatch.setattr(sg.settings, "security_gate_repos", "enduro-trails, foo")
    assert sg.security_gate_applies("enduro-trails") is True
    assert sg.security_gate_applies("orchestrator") is False
    # Kill-switch wins over everything.
    monkeypatch.setattr(sg.settings, "security_gate_enabled", False)
    assert sg.security_gate_applies("orchestrator") is False


def test_qg_wrapper_delegates(monkeypatch):
    """The QG wrapper delegates to security_gate.check_security_gate verbatim."""
    monkeypatch.setattr(sg, "check_security_gate", lambda r, w, b: (False, "delegated FAIL"))
    ok, reason = check_security_gate("orchestrator", _WI, _BRANCH)
    assert ok is False
    assert reason == "delegated FAIL"
