"""PR-* test plan T5 — prove the import-linter gate is genuinely
active, not vestigial.

The PR-01 / PR-06 contracts in ``pyproject.toml`` are STATIC AST
analysers; they only do their job if ``lint-imports`` runs and
its non-zero exit propagates to the gate. This test introduces
a synthetic violation (an import of ``PySide6`` from a tmpdir
package) and runs ``lint-imports`` against a config that targets
that package — assertion: exit non-zero + contract reported BROKEN.

If this test ever passes silently while the production contracts
pass-through (or vice-versa), the gate is broken in some
unforeseen way. The test catches:

  - lint-imports binary missing / not on PATH
  - import-linter config syntax that silently no-ops
  - lint-imports exit code not propagating
  - the violating import being undetected by AST analysis

Cost: one subprocess.run per test (~200ms); marker ``conformance``
runs in the release-gated tier.
"""

from __future__ import annotations

import shutil
import subprocess
import sys
from pathlib import Path

import pytest

pytestmark = pytest.mark.conformance

# Resolve from PATH first — a global `pip install` (CI / container) puts
# the entry point on PATH (e.g. /usr/local/bin/lint-imports), not under a
# project `.venv/`. Fall back to the venv layout for local `uv sync` dev
# where the venv may not be the active interpreter.
LINT_IMPORTS = Path(
    shutil.which("lint-imports") or (Path(__file__).resolve().parents[2] / ".venv/bin/lint-imports")
)


@pytest.fixture
def violation_root(tmp_path: Path) -> Path:
    """Create a synthetic Python package whose code imports a
    forbidden UI framework. Return the tmpdir root so the
    ``lint-imports`` config can refer to it via sys.path."""
    pkg = tmp_path / "_pr01_violation_fixture"
    pkg.mkdir()
    (pkg / "__init__.py").write_text(
        # The violating import. We don't need PySide6 to actually
        # be installed — import-linter does AST analysis. The
        # string "PySide6" in an import statement is the entire
        # signal it needs.
        "import PySide6  # type: ignore[import-not-found]  # noqa: F401\n",
        encoding="utf-8",
    )
    return tmp_path


def test_lint_imports_binary_exists() -> None:
    """Belt-and-braces: the lint-imports binary itself must exist
    in the venv. If it's missing, `scripts/gates` would silent-skip
    the layering gate; this test prevents that."""
    assert LINT_IMPORTS.exists(), (
        f"lint-imports binary not found at {LINT_IMPORTS}. "
        "Run `uv sync --all-extras` to install dev deps."
    )


def test_lint_imports_rejects_synthetic_violation(
    violation_root: Path,
    tmp_path: Path,
) -> None:
    """Inject a fake package that imports PySide6; build a
    minimal config naming it as source + forbidding PySide6;
    invoke lint-imports; expect exit non-zero + a BROKEN report."""
    config = tmp_path / "violation.cfg"
    # Use the INI-style import-linter config (not pyproject.toml) —
    # simpler to inject for the test, doesn't depend on tomli parsing.
    config.write_text(
        """\
[importlinter]
root_packages =
    _pr01_violation_fixture
include_external_packages = True

[importlinter:contract:forbid-pyside6]
name = T5 — synthetic PySide6 forbidden
type = forbidden
source_modules =
    _pr01_violation_fixture
forbidden_modules =
    PySide6
""",
        encoding="utf-8",
    )

    proc = subprocess.run(  # noqa: S603 — controlled fixture invocation
        [str(LINT_IMPORTS), "--config", str(config)],
        cwd=violation_root,
        capture_output=True,
        text=True,
        timeout=30,
        env={"PYTHONPATH": str(violation_root), "PATH": "/usr/bin:/bin"},
    )

    # The whole point of this test:
    assert proc.returncode != 0, (
        f"lint-imports MUST reject the synthetic violation but "
        f"returned 0. The gate is broken — UI imports could ship "
        f"silently. stdout={proc.stdout!r} stderr={proc.stderr!r}"
    )
    assert "BROKEN" in proc.stdout, (
        f"lint-imports rejected the violation but didn't mark it BROKEN in output: {proc.stdout!r}"
    )


def test_production_pr01_contract_is_currently_kept() -> None:
    """The contrapositive: the SDK's own PR-01 / PR-06 contracts
    in pyproject.toml MUST be currently KEPT. If this test fails,
    a real violation has landed somewhere in src/."""
    repo = Path(__file__).resolve().parents[2]
    proc = subprocess.run(  # noqa: S603 — controlled
        [str(LINT_IMPORTS), "--config", "pyproject.toml"],
        cwd=repo,
        capture_output=True,
        text=True,
        timeout=60,
    )
    assert proc.returncode == 0, (
        f"production import-linter contract is BROKEN. PR-01 or PR-06 "
        f"has a live violation: stdout={proc.stdout!r}"
    )
    # The success-line text is "N kept, 0 broken." — any non-zero
    # count after "broken" is a regression.
    assert "0 broken" in proc.stdout, (
        f"production contract output didn't report '0 broken': {proc.stdout!r}"
    )


_ = sys  # silence "unused-import" if sys reference removed
