"""Unit tests for scripts/approve_proposal.py."""

from __future__ import annotations

import sys
from pathlib import Path

try:
    import pytest
except ImportError:  # standalone fallback so the file runs without pytest installed
    import contextlib
    import types

    pytest = types.SimpleNamespace()

    @contextlib.contextmanager
    def _raises(exc, match=None):
        try:
            yield
        except exc as err:
            if match is not None and match not in str(err):
                raise AssertionError(f"expected {match!r} in {err!r}") from err
            return
        raise AssertionError(f"expected {exc.__name__} to be raised")

    pytest.raises = _raises

REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))

import approve_proposal as approve  # noqa: E402


def _strict_pending():
    return {
        "id": "00957f6c-9883-4015-95a4-6b09367efb0e",
        "proposal_type": "add_edge",
        "status": "pending_review",
        "payload": {
            "apply_payload": {
                "from_claim": "11111111-1111-1111-1111-111111111111",
                "to_claim": "22222222-2222-2222-2222-222222222222",
                "edge_type": "supports",
            }
        },
    }


def test_assert_approvable_allows_strict_pending_payload():
    approve.assert_approvable(_strict_pending(), "m3ta", "approved from Telegram")


def test_assert_approvable_rejects_missing_review_note():
    with pytest.raises(SystemExit):
        approve.assert_approvable(_strict_pending(), "m3ta", "")


def test_assert_approvable_rejects_empty_reviewer():
    with pytest.raises(SystemExit):
        approve.assert_approvable(_strict_pending(), " ", "approved")


def test_assert_approvable_rejects_already_approved():
    proposal = _strict_pending()
    proposal["status"] = "approved"
    with pytest.raises(SystemExit):
        approve.assert_approvable(proposal, "m3ta", "approved")


def test_assert_approvable_rejects_non_applyable_type():
    proposal = _strict_pending()
    proposal["proposal_type"] = "rewrite_claim"
    with pytest.raises(SystemExit):
        approve.assert_approvable(proposal, "m3ta", "approved")


def test_assert_approvable_rejects_freeform_without_apply_payload():
    proposal = _strict_pending()
    proposal["payload"] = {"rationale": "looks approved in chat"}
    with pytest.raises(SystemExit):
        approve.assert_approvable(proposal, "m3ta", "approved")


def test_build_approve_sql_has_required_guards():
    proposal = _strict_pending()
    sql = approve.build_approve_sql(proposal, "m3ta", "O'Brien approves")
    assert "kb_stage.approve_strict_proposal" in sql
    assert "update kb_stage.kb_proposals" not in sql.lower()
    assert "E'00957f6c-9883-4015-95a4-6b09367efb0e'::uuid" in sql
    assert "E'add_edge'" in sql
    assert "edge_type" in sql
    assert "supports" in sql
    assert "E'O''Brien approves'" in sql
    assert "set local standard_conforming_strings = on" in sql.lower()

    prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8")
    for proposal_type in ("add_edge", "attach_evidence", "revise_strategy", "approve_claim"):
        assert f"'{proposal_type}'" in prereqs


def test_approval_prereqs_bind_exact_payload_and_authenticated_principal():
    prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8").lower()
    assert "p_expected_proposal_type" in prereqs
    assert "p_expected_payload" in prereqs
    assert "payload = p_expected_payload" in prereqs
    assert "kb_review_principals" in prereqs
    assert "session_user::name" in prereqs
    assert "kb_proposal_approvals" in prereqs
    assert "owner to kb_gate_owner" in prereqs
    assert "set search_path = pg_catalog, pg_temp" in prereqs


def test_review_cli_uses_separate_role_and_secret_defaults():
    args = approve.parse_args(
        [
            "00957f6c-9883-4015-95a4-6b09367efb0e",
            "--reviewed-by", "m3ta",
            "--review-note", "approved",
        ]
    )
    assert args.role == "kb_review"
    assert args.secrets_file.endswith("kb-review.env")

    prereqs = (REPO_ROOT / "scripts" / "kb_apply_prereqs.sql").read_text(encoding="utf-8").lower()
    assert "revoke update on kb_stage.kb_proposals from kb_apply" in prereqs
    assert "grant execute on function kb_stage.approve_strict_proposal" in prereqs
    assert "to kb_review" in prereqs
    assert "grant execute on function kb_stage.assert_approved_proposal" in prereqs
    assert "grant execute on function kb_stage.finish_approved_proposal" in prereqs
    assert "to kb_apply" in prereqs


def test_parse_reviewed_rows_requires_exactly_one_row():
    rows = approve.parse_reviewed_rows(
        '[{"id":"00957f6c-9883-4015-95a4-6b09367efb0e","status":"approved"}]',
        "00957f6c-9883-4015-95a4-6b09367efb0e",
    )
    assert rows[0]["status"] == "approved"
    with pytest.raises(SystemExit):
        approve.parse_reviewed_rows("[]", "00957f6c-9883-4015-95a4-6b09367efb0e")


if __name__ == "__main__":
    import traceback

    failures = 0
    for name, fn in sorted(globals().items()):
        if name.startswith("test_") and callable(fn):
            try:
                fn()
                print(f"PASS {name}")
            except Exception:
                failures += 1
                print(f"FAIL {name}")
                traceback.print_exc()
    sys.exit(1 if failures else 0)
