"""Integration tests covering Codex bundled-skill install behavior.

The full ``clud --codex`` install path runs only when launch setup elects the
``Global`` scope, which requires an interactive TTY on both stdin and stderr
(see ``setup_interactive`` in ``crates/clud-bin/src/main.rs``). Subprocess
tests cannot easily synthesize that, so the byte-for-byte install assertions
live as Rust unit tests in ``crates/clud-bin/src/skills.rs::tests`` and the
end-to-end launch-setup flow lives in
``crates/clud-bin/src/launch_setup.rs::tests``. This file holds the
subprocess-scope checks: the binary builds, parses ``--codex`` flag-paths
without crashing, and the embedded ``SKILL.md`` source matches what the
installer would write.
"""

from __future__ import annotations

from pathlib import Path

import pytest

from tests import process

pytestmark = pytest.mark.integration

REPO_ROOT = Path(__file__).resolve().parents[2]
BUNDLED_SKILLS_DIR = REPO_ROOT / "crates" / "clud-bin" / "assets" / "skills"


def test_every_bundled_skill_carries_managed_marker_and_frontmatter() -> None:
    """Every source-of-truth SKILL.md must carry the marker the installer keys
    its purge and refresh on. If this drifts, every install/purge guarantee in
    ``skills.rs`` silently weakens.

    Globbed rather than named: this test used to point at clud-pr specifically
    and would have been orphaned outright when that skill was retired. The
    directory is the contract, not any one skill in it.
    """
    skills = sorted(BUNDLED_SKILLS_DIR.glob("*/SKILL.md"))
    assert skills, f"no bundled skills found under {BUNDLED_SKILLS_DIR}"

    for skill in skills:
        body = skill.read_text(encoding="utf-8")
        assert body.startswith("---"), (
            f"{skill.parent.name}/SKILL.md must begin with YAML frontmatter"
        )
        assert "<!-- managed-by: clud -->" in body, (
            f"{skill.parent.name} is missing the managed-by marker, which "
            "purge_stale_agents_skills and the refresh path both key on"
        )


def test_clud_codex_dry_run_does_not_crash(
    clud_binary: Path, mock_env: dict[str, str], tmp_path: Path
) -> None:
    """A ``--codex --dry-run -p`` invocation must parse and emit a launch plan
    without writing skill files. This guards against regressions where the
    Codex backend path is broken in arg parsing or LaunchPlan construction."""
    result = process.run(
        [str(clud_binary), "--codex", "--dry-run", "-p", "noop prompt"],
        capture_output=True,
        text=True,
        timeout=30,
        env=mock_env,
        cwd=tmp_path,
    )
    assert result.returncode == 0, f"stderr: {result.stderr}\nstdout: {result.stdout}"
    # --dry-run emits a JSON LaunchPlan to stdout.
    assert '"backend"' in result.stdout, f"missing backend field: {result.stdout!r}"
    assert "codex" in result.stdout.lower()
    # Dry-run is a non-prompting launch, so no global-setup files should land.
    home_env = mock_env.get("HOME") or mock_env.get("USERPROFILE")
    if home_env:
        # We did not pre-create ~/.codex, and dry-run uses session-only scope,
        # so the install path should never touch a real home dir.
        assert not (tmp_path / ".codex" / "skills").exists()
        assert not (tmp_path / ".agents" / "skills").exists()
