"""ORCH-057 D3: startup-hook observability + the clear pre-launch outcome.

TC-10 / TC-11 (04-test-plan.yaml):
  * TC-10 — the lifespan startup hook, on a detected mismatch, emits a WARNING and a
    Telegram message; a detect error never crashes the start (never-fatal).
  * TC-11 — the "clear, early" outcome on a permission failure is delivered by the
    actionable ensure_worktree error (ADR-001 D3: claim is NOT blocked), i.e. the
    launch surfaces an actionable diagnosis, never a raw git-fatal.

Background daemons are disabled via env so the lifespan is cheap and deterministic.
"""
import os
import tempfile

import pytest

_test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_fsn_startup.db")
os.environ["ORCH_DB_PATH"] = _test_db
os.environ["ORCH_REPOS_DIR"] = tempfile.gettempdir()
os.environ["ORCH_GITEA_TOKEN"] = "test-token"
os.environ["ORCH_PLANE_API_TOKEN"] = "test-token"
os.environ["ORCH_PLANE_WEBHOOK_SECRET"] = ""
os.environ["ORCH_GITEA_WEBHOOK_SECRET"] = ""
# Keep the lifespan light: no background daemons during the test.
os.environ["ORCH_RECONCILE_ENABLED"] = "false"
os.environ["ORCH_REAPER_ENABLED"] = "false"
os.environ["ORCH_DISK_MONITOR_ENABLED"] = "false"
os.environ["ORCH_BUILD_CACHE_PRUNE_ENABLED"] = "false"
os.environ["ORCH_FS_NORMALIZE_ENABLED"] = "true"

from fastapi.testclient import TestClient

from src import fs_normalize, git_worktree
from src.main import app
from src.db import init_db


@pytest.fixture(autouse=True)
def _db():
    if os.path.exists(_test_db):
        os.unlink(_test_db)
    init_db()
    fs_normalize.reset_cache()
    yield
    if os.path.exists(_test_db):
        os.unlink(_test_db)


# ---------------------------------------------------------------------------
# TC-10 — startup observability
# ---------------------------------------------------------------------------
def test_tc10_startup_mismatch_warns_and_telegrams(monkeypatch, caplog):
    """TC-10: on a detected mismatch the startup hook logs a WARNING and sends a
    Telegram message (mocked)."""
    sent = []
    monkeypatch.setattr(
        "src.notifications.send_telegram", lambda *a, **k: sent.append(a[0] if a else "")
    )
    scan = fs_normalize.OwnershipScan(
        mismatch=True, target_uid=1000, roots_checked=["/repos/_wt"],
        roots_mismatch=["/repos/_wt"], sample_path="/repos/_wt/x", checked_at=1.0,
    )
    monkeypatch.setattr("src.fs_normalize.scan_ownership", lambda *a, **k: scan)

    with caplog.at_level("WARNING"):
        with TestClient(app):
            pass

    assert any("FS-ownership mismatch" in r.message for r in caplog.records)
    # Filter for the fs-ownership message (the shared startup may emit other,
    # unrelated Telegram traffic — e.g. a leftover task's tracker card).
    fs_msgs = [m for m in sent if "legacy root-owned" in m.lower() or "chown" in m.lower()]
    assert fs_msgs, "expected a Telegram message on mismatch"


def test_tc10_startup_detect_error_never_fatal(monkeypatch):
    """TC-10: a detect error must NOT crash the start (never-fatal)."""
    def boom(*a, **k):
        raise RuntimeError("simulated detect failure")

    monkeypatch.setattr("src.fs_normalize.scan_ownership", boom)
    # Entering/exiting the lifespan must not raise.
    with TestClient(app):
        pass


def test_tc10_startup_clean_no_telegram(monkeypatch):
    """A clean environment (no mismatch) sends no Telegram and does not warn."""
    sent = []
    monkeypatch.setattr(
        "src.notifications.send_telegram", lambda *a, **k: sent.append(a[0] if a else "")
    )
    clean = fs_normalize.OwnershipScan(mismatch=False, target_uid=1000, checked_at=1.0)
    monkeypatch.setattr("src.fs_normalize.scan_ownership", lambda *a, **k: clean)
    with TestClient(app):
        pass
    # No fs-ownership message on a clean environment (unrelated startup Telegram
    # traffic from a shared-DB leftover task is ignored).
    fs_msgs = [m for m in sent if "legacy root-owned" in m.lower() or "обнаружены legacy" in m.lower()]
    assert fs_msgs == []


# ---------------------------------------------------------------------------
# TC-11 — clear pre-launch outcome (D1, not a claim gate)
# ---------------------------------------------------------------------------
def test_tc11_launch_permission_failure_is_actionable_not_raw(tmp_path, monkeypatch):
    """TC-11: the launch-time worktree creation surfaces an actionable error (clear,
    before the agent spends a token), not a raw git-fatal — the ADR-001 D3 "внятно и
    заранее" outcome that replaces a blocking claim gate."""
    repo = "orchestrator"
    repos_dir = tmp_path / "repos"
    (repos_dir / repo).mkdir(parents=True)
    monkeypatch.setattr(git_worktree.settings, "repos_dir", str(repos_dir))
    monkeypatch.setattr(git_worktree.settings, "worktrees_dir", str(repos_dir / "_wt"))
    monkeypatch.setattr(git_worktree.settings, "fs_normalize_enabled", True)

    class _R:
        def __init__(self, rc, err=""):
            self.returncode = rc
            self.stderr = err
            self.stdout = ""

    def fake_run(cmd, *a, **k):
        if "fetch" in cmd:
            return _R(0)
        if "worktree" in cmd and "add" in cmd:
            return _R(128, "fatal: ...: Permission denied")
        return _R(0)

    monkeypatch.setattr(git_worktree.subprocess, "run", fake_run)

    with pytest.raises(RuntimeError) as ei:
        git_worktree.ensure_worktree(repo, "feature/x")
    msg = str(ei.value)
    assert "INFRA.md" in msg and "chown" in msg.lower()
    assert "git worktree add failed" not in msg  # not the raw passthrough
