"""ORCH-019 — src/bug_fast_track.py: bug-fast-track pure logic (never-raise, fail-safe).

Covers (04-test-plan.yaml):
  TC-01  is_bug_task() True for an issue carrying the `Bug` label (label read from
         the Plane API via labels.has_label, NOT the webhook payload).
  TC-02  is_bug_task() False on missing/ambiguous label or labels=None (fail-safe).
  TC-03  bug_fast_track_applies(): the LOCAL scope (enabled + CSV repos) is checked
         FIRST, before any network; disabled flag -> False without has_label.
  TC-04  never-raise: an exception in the label apparatus degrades is_bug_task to
         False (full cycle), never propagates.
"""
import os
import tempfile

import pytest

os.environ.setdefault(
    "ORCH_DB_PATH", os.path.join(tempfile.gettempdir(), "test_bug_fast_track.db")
)
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")

from src import bug_fast_track  # noqa: E402
from src import plane_sync  # noqa: E402
from src import config as cfg  # noqa: E402


@pytest.fixture(autouse=True)
def enabled_self_hosting(monkeypatch):
    monkeypatch.setattr(cfg.settings, "bug_fast_track_enabled", True, raising=False)
    monkeypatch.setattr(cfg.settings, "bug_fast_track_label", "Bug", raising=False)
    monkeypatch.setattr(cfg.settings, "bug_fast_track_repos", "", raising=False)
    # Keep _resolve_project_id offline-deterministic (mirrors test_labels.py).
    monkeypatch.setattr(plane_sync, "_resolve_project_id", lambda w=None, p=None: "proj-1")
    yield


# --- TC-01: classification True --------------------------------------------
def test_tc01_is_bug_task_true(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-BUG"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"})
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is True


def test_tc01_label_from_plane_api_not_payload(monkeypatch):
    """The decision comes from labels.has_label (Plane API), independent of any
    webhook payload field — a payload `type` is irrelevant."""
    seen = {"fetch": 0}

    def fetch(w, p=None):
        seen["fetch"] += 1
        return ["uuid-BUG"]
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", fetch)
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"})
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is True
    assert seen["fetch"] == 1  # the Plane API WAS consulted


# --- TC-02: fail-safe on absent / ambiguous / None -------------------------
def test_tc02_label_absent(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-OTHER"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"})
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False


def test_tc02_labels_none(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: None)
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"})
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False


def test_tc02_label_ambiguous(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-BUG"])
    monkeypatch.setattr(
        plane_sync, "get_project_labels", lambda pid: {"bug": "__AMBIGUOUS__"}
    )
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False


def test_tc02_empty_label_config(monkeypatch):
    monkeypatch.setattr(cfg.settings, "bug_fast_track_label", "", raising=False)
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-BUG"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"})
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False


# --- TC-03: local scope first (CSV + self-hosting + kill-switch) ------------
def test_tc03_empty_csv_self_hosting_only(monkeypatch):
    monkeypatch.setattr(cfg.settings, "bug_fast_track_repos", "", raising=False)
    assert bug_fast_track.bug_fast_track_applies("orchestrator") is True
    assert bug_fast_track.bug_fast_track_applies("enduro-trails") is False


def test_tc03_csv_membership(monkeypatch):
    monkeypatch.setattr(cfg.settings, "bug_fast_track_repos", "enduro-trails, foo", raising=False)
    assert bug_fast_track.bug_fast_track_applies("enduro-trails") is True
    assert bug_fast_track.bug_fast_track_applies("foo") is True
    # orchestrator is NOT in the explicit CSV -> out of scope.
    assert bug_fast_track.bug_fast_track_applies("orchestrator") is False


def test_tc03_killswitch_off_no_network(monkeypatch):
    """The gate idiom `applies(repo) and is_bug_task(...)` short-circuits before any
    network call when the kill-switch is off (AC-6)."""
    monkeypatch.setattr(cfg.settings, "bug_fast_track_enabled", False, raising=False)
    called = {"fetch": 0}

    def spy(*a, **k):
        called["fetch"] += 1
        return ["uuid-BUG"]
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", spy)

    repo = "orchestrator"
    fired = bug_fast_track.bug_fast_track_applies(repo) and bug_fast_track.is_bug_task(
        "ORCH-1", "proj-1"
    )
    assert fired is False
    assert called["fetch"] == 0  # is_bug_task never reached -> zero network


# --- TC-04: never-raise -----------------------------------------------------
def test_tc04_is_bug_task_never_raises(monkeypatch):
    def boom(*a, **k):
        raise RuntimeError("plane down")
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", boom)
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"})
    # Degrades to False (full cycle), no exception.
    assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False


def test_tc04_applies_never_raises(monkeypatch):
    # A repos config whose access explodes still yields False, not a crash.
    class _Poisoned:
        bug_fast_track_enabled = True

        @property
        def bug_fast_track_repos(self):
            raise RuntimeError("boom")

    monkeypatch.setattr(bug_fast_track, "settings", _Poisoned(), raising=False)
    assert bug_fast_track.bug_fast_track_applies("orchestrator") is False


# --- skips_architecture predicate ------------------------------------------
def test_skips_architecture_bug(monkeypatch):
    assert bug_fast_track.skips_architecture("bug") is True
    assert bug_fast_track.skips_architecture("BUG") is True


def test_skips_architecture_full(monkeypatch):
    assert bug_fast_track.skips_architecture("full") is False
    assert bug_fast_track.skips_architecture(None) is False
    assert bug_fast_track.skips_architecture("") is False


def test_skips_architecture_killswitch_off(monkeypatch):
    monkeypatch.setattr(cfg.settings, "bug_fast_track_enabled", False, raising=False)
    # Even a stored 'bug' track is inert when the kill-switch is off (1:1 routing).
    assert bug_fast_track.skips_architecture("bug") is False


# --- snapshot ---------------------------------------------------------------
def test_snapshot_never_raises():
    snap = bug_fast_track.snapshot()
    assert set(snap) >= {
        "enabled", "label", "repos",
        "active_bug_tasks", "total_bug_tasks", "est_saved_architecture_runs",
    }
