"""ORCH-089 — src/labels.py: auto-mode pure logic (never-raise, fail-safe).

Covers (04-test-plan.yaml):
  TC-01  has_label True when the label is present on the issue.
  TC-02  has_label False when the label is absent.
  TC-03  has_label fail-safe (no auto, never-raise) on Plane API error/timeout.
  TC-04  label-name matching is normalized (case/space); ambiguity -> no auto.
  TC-05  auto_approve_applies / auto_deploy_applies: CSV scope + self-hosting.
  TC-06  auto_label_enabled=False -> applies() False -> has_label never reached
         (no network call on the gate).
"""
import os
import tempfile

import pytest

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

from src import labels  # 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, "auto_label_enabled", True, raising=False)
    monkeypatch.setattr(cfg.settings, "auto_approve_label", "autoApprove", raising=False)
    monkeypatch.setattr(cfg.settings, "auto_deploy_label", "autoDeploy", raising=False)
    monkeypatch.setattr(cfg.settings, "auto_label_repos", "", raising=False)
    # Keep _resolve_project_id offline-deterministic.
    monkeypatch.setattr(plane_sync, "_resolve_project_id", lambda w=None, p=None: "proj-1")
    yield


# --- TC-01 / TC-02 ---------------------------------------------------------
def test_tc01_has_label_present(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-A"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"autoapprove": "uuid-A"})
    assert labels.has_label("ORCH-1", "autoApprove") is True


def test_tc02_has_label_absent(monkeypatch):
    # Issue carries a different label uuid than the project's autoApprove uuid.
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-OTHER"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"autoapprove": "uuid-A"})
    assert labels.has_label("ORCH-1", "autoApprove") is False


def test_tc02_has_label_empty_issue_labels(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: [])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"autoapprove": "uuid-A"})
    assert labels.has_label("ORCH-1", "autoApprove") is False


# --- TC-03: fail-safe / never-raise ----------------------------------------
def test_tc03_fetch_none_failsafe(monkeypatch):
    # fetch returns None (could-not-read) -> False, no auto.
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: None)
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"autoapprove": "uuid-A"})
    assert labels.has_label("ORCH-1", "autoApprove") is False


def test_tc03_fetch_raises_failsafe(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: {"autoapprove": "uuid-A"})
    # Never raises; degrades to no auto.
    assert labels.has_label("ORCH-1", "autoApprove") is False


def test_tc03_project_map_empty_failsafe(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-A"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {})
    assert labels.has_label("ORCH-1", "autoApprove") is False


# --- TC-04: normalization + ambiguity --------------------------------------
def test_tc04_normalized_match(monkeypatch):
    # Issue label uuid-A; project maps a differently-cased/spaced name to it.
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-A"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"autoapprove": "uuid-A"})
    # Sought name has different case + surrounding spaces.
    assert labels.has_label("ORCH-1", "  AUTOapprove ") is True


def test_tc04_ambiguous_name_no_auto(monkeypatch):
    # Two distinct project labels collapse to the same normalized name -> ambiguous
    # sentinel from get_project_labels -> has_label False.
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-A"])
    monkeypatch.setattr(
        plane_sync, "get_project_labels",
        lambda pid: {"autoapprove": "__AMBIGUOUS__"},
    )
    assert labels.has_label("ORCH-1", "autoApprove") is False


def test_tc04_empty_label_name(monkeypatch):
    monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-A"])
    monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"autoapprove": "uuid-A"})
    assert labels.has_label("ORCH-1", "") is False


# --- TC-05: scope (CSV + self-hosting) -------------------------------------
def test_tc05_empty_csv_self_hosting_only(monkeypatch):
    monkeypatch.setattr(cfg.settings, "auto_label_repos", "", raising=False)
    assert labels.auto_approve_applies("orchestrator") is True
    assert labels.auto_deploy_applies("orchestrator") is True
    # Non-self repo with empty CSV -> not in scope.
    assert labels.auto_approve_applies("enduro-trails") is False
    assert labels.auto_deploy_applies("enduro-trails") is False


def test_tc05_csv_membership(monkeypatch):
    monkeypatch.setattr(cfg.settings, "auto_label_repos", "enduro-trails, foo", raising=False)
    assert labels.auto_approve_applies("enduro-trails") is True
    assert labels.auto_deploy_applies("foo") is True
    # orchestrator is NOT in the explicit CSV -> out of scope.
    assert labels.auto_approve_applies("orchestrator") is False


# --- TC-06: kill-switch -> applies False, no network -----------------------
def test_tc06_killswitch_applies_false(monkeypatch):
    monkeypatch.setattr(cfg.settings, "auto_label_enabled", False, raising=False)
    assert labels.auto_approve_applies("orchestrator") is False
    assert labels.auto_deploy_applies("orchestrator") is False


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

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

    repo = "orchestrator"
    fired = labels.auto_approve_applies(repo) and labels.has_label("ORCH-1", "autoApprove")
    assert fired is False
    assert called["fetch"] == 0  # has_label never reached -> zero network


def test_snapshot_never_raises():
    snap = labels.snapshot()
    assert set(snap) >= {"enabled", "approve_label", "deploy_label", "repos"}
