"""Integration: build the wheel, install it into an isolated venv, and prove
the installed distribution is self-contained.

This is the boundary a source-tree existence test can't reach (issue #197
review): under an installed wheel the package lives in ``site-packages`` with
no repo root above it, so anything resolving Dockerfiles / nix / scripts from
``__file__``'s parents would break. Here we install for real and assert that
``bot-bottle doctor`` runs from the console script and that
``bot_bottle.resources`` stages a valid, repo-root-shaped build context.

It does NOT run `start` — building images needs a Docker/KVM host (CI). It
skips cleanly when the build/venv toolchain isn't available.
"""

from __future__ import annotations

import subprocess
import sys
import tempfile
import unittest
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]


class TestWheelInstall(unittest.TestCase):
    """`build` is a declared dev dependency (requirements-dev.txt), so this runs
    in CI. A build/install failure is a real packaging regression and FAILS —
    only genuinely-unsupported infra (no `venv`/`ensurepip`) skips."""

    _tmp: "tempfile.TemporaryDirectory[str]"
    venv_py: Path
    app_root: Path

    @classmethod
    def setUpClass(cls):
        cls._tmp = tempfile.TemporaryDirectory(  # pylint: disable=consider-using-with
            prefix="bb-wheel-")
        tmp = Path(cls._tmp.name)
        dist = tmp / "dist"

        # A failed wheel build is exactly the regression this test guards — fail,
        # don't skip. `build` is installed via requirements-dev.txt.
        built = subprocess.run(
            [sys.executable, "-m", "build", "--wheel", "--outdir", str(dist), str(REPO_ROOT)],
            capture_output=True, text=True, check=False,
        )
        if built.returncode != 0:
            raise AssertionError(f"wheel build failed:\n{built.stderr[-2000:]}")
        wheels = list(dist.glob("*.whl"))
        if not wheels:
            raise AssertionError(f"no wheel produced:\n{built.stdout[-2000:]}")

        # A missing `venv`/`ensurepip` is unsupported optional infra, not a
        # packaging bug — skip only here.
        venv = tmp / "venv"
        made = subprocess.run([sys.executable, "-m", "venv", str(venv)],
                              capture_output=True, text=True, check=False)
        if made.returncode != 0:
            raise unittest.SkipTest(f"venv/ensurepip unavailable:\n{made.stderr[-1500:]}")
        cls.venv_py = venv / "bin" / "python"

        # Installing the freshly-built wheel must succeed — fail if it doesn't.
        install = subprocess.run(
            [str(cls.venv_py), "-m", "pip", "install", "--quiet", str(wheels[0])],
            capture_output=True, text=True, check=False,
        )
        if install.returncode != 0:
            raise AssertionError(f"pip install of the wheel failed:\n{install.stderr[-2000:]}")

        # Isolate the staged build root the wheel writes under the app-data dir.
        cls.app_root = tmp / "appdata"

    @classmethod
    def tearDownClass(cls):
        cls._tmp.cleanup()

    def _run(self, tail: "list[str]") -> "subprocess.CompletedProcess[str]":
        """Run the installed venv's python with `tail` appended, from a neutral
        cwd so the source checkout isn't on sys.path — we must import the
        *installed* package, not the repo we built from."""
        env = {"BOT_BOTTLE_ROOT": str(self.app_root), "PATH": "/usr/bin:/bin"}
        return subprocess.run(
            [str(self.venv_py), *tail],
            capture_output=True, text=True, env=env, cwd=self._tmp.name, check=False,
        )

    def test_console_entry_point_installed(self):
        # The `bot-bottle` script the wheel declares must exist in the venv.
        script = self.venv_py.parent / "bot-bottle"
        self.assertTrue(script.is_file(), "bot-bottle console script not installed")

    def test_doctor_runs_from_installed_package(self):
        proc = self._run(["-m", "bot_bottle.cli", "doctor"])
        # doctor exits non-zero here (no backend), but it must RUN and report.
        self.assertIn("python", proc.stdout)
        self.assertIn(proc.returncode, (0, 1))

    def test_installed_wheel_is_self_contained(self):
        # From the installed layout (not a checkout), resources must resolve
        # Dockerfiles and stage a repo-root-shaped build context.
        script = (
            "import bot_bottle.resources as r\n"
            "assert not r.is_source_checkout(), 'should not look like a checkout'\n"
            "assert r.dockerfile('Dockerfile.gateway').is_file()\n"
            "assert r.nix_netpool_module().is_file()\n"
            "assert r.netpool_script().is_file()\n"
            "root = r.build_root()\n"
            "assert (root / 'bot_bottle' / '__init__.py').is_file(), 'no package in context'\n"
            "assert (root / 'pyproject.toml').is_file(), 'no pyproject in context'\n"
            "assert (root / 'Dockerfile.gateway').is_file(), 'no Dockerfile in context'\n"
            "print('SELF_CONTAINED_OK')\n"
        )
        proc = self._run(["-c", script])
        self.assertIn("SELF_CONTAINED_OK", proc.stdout, msg=proc.stderr)


if __name__ == "__main__":
    unittest.main()
