"""ORCH-036 TC-01/02/03: deterministic exit-code -> deploy_status mapping.

The finalizer (Phase C) maps the host-hook exit-code to the machine verdict via a
PURE function (no LLM, no I/O), so it is unit-testable in isolation. Contract
(hook exit-code 0/1/2, AC-1/AC-3): 0 -> SUCCESS; 1 (rolled back), 2 (rollback also
failed), and anything else -> FAILED (fail-closed).
"""

import os

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

from src import self_deploy  # noqa: E402
from src.self_deploy import map_exit_code_to_status, build_deploy_log  # noqa: E402


def test_tc01_exit0_maps_to_success():
    assert map_exit_code_to_status(0) == "SUCCESS"


def test_tc02_exit1_rolled_back_maps_to_failed():
    assert map_exit_code_to_status(1) == "FAILED"


def test_tc03_exit2_rollback_also_failed_maps_to_failed():
    assert map_exit_code_to_status(2) == "FAILED"


def test_tc09_provenance_fail_closed_exit1_maps_to_failed():
    """ORCH-058 TC-09: the Strategy-B hook fail-close uses `exit 1`; that must map
    to FAILED so the existing БАГ-8 rollback path triggers (prod never left stale)."""
    assert map_exit_code_to_status(1) == "FAILED"


def test_other_exit_codes_map_to_failed():
    for code in (3, 127, 255, -1):
        assert map_exit_code_to_status(code) == "FAILED"


def test_non_int_or_none_maps_to_failed_fail_closed():
    assert map_exit_code_to_status(None) == "FAILED"
    assert map_exit_code_to_status("garbage") == "FAILED"


def test_deploy_log_frontmatter_carries_status():
    """The rendered log must expose deploy_status in YAML frontmatter so the
    existing _parse_deploy_status contract (AC-10) reads the right verdict."""
    body_ok = build_deploy_log("ORCH-036", 0, "SUCCESS")
    assert body_ok.startswith("---\n")
    assert "deploy_status: SUCCESS" in body_ok
    body_fail = build_deploy_log("ORCH-036", 2, "FAILED")
    assert "deploy_status: FAILED" in body_fail
    assert "hook_exit_code: 2" in body_fail


def test_clear_state_removes_all_markers_and_is_idempotent(monkeypatch, tmp_path):
    """clear_state wipes the whole work-item state dir (all sentinels) and treats a
    missing dir as success, so a re-deploy after rollback starts from a clean slate."""
    monkeypatch.setattr(self_deploy.settings, "repos_dir", str(tmp_path))
    repo, wi = "orchestrator", "ORCH-036"
    self_deploy.write_marker(repo, wi, self_deploy.APPROVE_REQUESTED, "t")
    self_deploy.write_marker(repo, wi, self_deploy.INITIATED, "t")
    self_deploy.write_marker(repo, wi, self_deploy.RESULT, "1")
    assert self_deploy.has_marker(repo, wi, self_deploy.INITIATED) is True

    assert self_deploy.clear_state(repo, wi) is True
    assert self_deploy.has_marker(repo, wi, self_deploy.APPROVE_REQUESTED) is False
    assert self_deploy.has_marker(repo, wi, self_deploy.INITIATED) is False
    assert self_deploy.has_marker(repo, wi, self_deploy.RESULT) is False
    # Idempotent: clearing an already-absent dir is still success (never raises).
    assert self_deploy.clear_state(repo, wi) is True
