from __future__ import annotations

import json
import os
from pathlib import Path
import subprocess
import sys
import sysconfig
import tempfile
import unittest


class WheelRuntimeTests(unittest.TestCase):
    def test_built_wheel_contains_and_runs_core_migrations_without_source_checkout(self) -> None:
        repository_root = Path(__file__).resolve().parents[1]
        with tempfile.TemporaryDirectory(prefix="govoplan-core-wheel-") as directory:
            temporary_root = Path(directory)
            wheel_directory = temporary_root / "wheels"
            wheel_directory.mkdir()
            self._run(
                sys.executable,
                "-m",
                "pip",
                "wheel",
                "--no-deps",
                "--wheel-dir",
                str(wheel_directory),
                str(repository_root),
                cwd=temporary_root,
            )
            core_wheels = tuple(wheel_directory.glob("govoplan_core-*.whl"))
            self.assertEqual(1, len(core_wheels))

            virtual_environment = temporary_root / "venv"
            self._run(sys.executable, "-m", "venv", str(virtual_environment), cwd=temporary_root)
            venv_python = virtual_environment / "bin" / "python"
            self._run(
                str(venv_python),
                "-m",
                "pip",
                "install",
                "--no-deps",
                str(core_wheels[0]),
                cwd=temporary_root,
            )

            database_path = temporary_root / "wheel-runtime.db"
            probe = self._run(
                str(venv_python),
                "-c",
                self._probe_script(),
                str(database_path),
                cwd=temporary_root,
                env={
                    **os.environ,
                    # Reuse only third-party test dependencies from the parent
                    # environment. Editable .pth files are not processed from
                    # PYTHONPATH, so Core itself can only come from the wheel.
                    "PYTHONPATH": sysconfig.get_path("purelib"),
                    "GOVOPLAN_CORE_SOURCE_ROOT": "",
                    "GOVOPLAN_ENABLED_MODULES": "",
                },
            )
            result = json.loads(probe.stdout)
            installed_package = Path(result["package_file"])
            runtime_root = Path(result["runtime_root"])
            self.assertTrue(installed_package.is_relative_to(virtual_environment))
            self.assertTrue(runtime_root.is_relative_to(virtual_environment))
            self.assertNotEqual(repository_root, runtime_root)
            self.assertTrue((runtime_root / "alembic.ini").is_file())
            self.assertTrue((runtime_root / "alembic" / "env.py").is_file())
            self.assertEqual(["d03a7b9c1e5f"], result["heads"])
            self.assertIn("core_scopes", result["tables"])
            self.assertIn("core_system_settings", result["tables"])

    @staticmethod
    def _run(*command: str, cwd: Path, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]:
        result = subprocess.run(
            command,
            cwd=cwd,
            env=env,
            check=False,
            capture_output=True,
            text=True,
        )
        if result.returncode:
            raise AssertionError(
                f"Command failed ({result.returncode}): {' '.join(command)}\n"
                f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
            )
        return result

    @staticmethod
    def _probe_script() -> str:
        return """
import json
from pathlib import Path
import sys

from alembic import command
from alembic.script import ScriptDirectory
from sqlalchemy import create_engine, inspect

import govoplan_core
from govoplan_core.db.migrations import _repo_root, alembic_config

database_url = f"sqlite:///{Path(sys.argv[1])}"
config = alembic_config(database_url=database_url, enabled_modules=())
command.upgrade(config, "heads")
script = ScriptDirectory.from_config(config)
engine = create_engine(database_url)
try:
    with engine.connect() as connection:
        tables = sorted(inspect(connection).get_table_names())
finally:
    engine.dispose()
print(json.dumps({
    "package_file": govoplan_core.__file__,
    "runtime_root": str(_repo_root()),
    "heads": sorted(script.get_heads()),
    "tables": tables,
}))
"""


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