import unittest
import sys
from pathlib import Path
from importlib.util import spec_from_file_location, module_from_spec


def load_script_module():
    """
    Import the script under test from roles/sys-ctl-rpr-docker-hard/files/script.py
    """
    test_file = Path(__file__).resolve()
    repo_root = test_file.parents[5]  # .../tests/unit/roles/sys-ctl-rpr-docker-hard/files -> repo root
    script_path = repo_root / "roles" / "sys-ctl-rpr-docker-hard" / "files" / "script.py"
    if not script_path.exists():
        raise FileNotFoundError(f"script.py not found at {script_path}")
    spec = spec_from_file_location("rpr_hard_script", str(script_path))
    mod = module_from_spec(spec)
    assert spec.loader is not None
    spec.loader.exec_module(mod)  # type: ignore[attr-defined]
    return mod


class TestRepairDockerHard(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.script = load_script_module()

    def test_detect_env_file_priority(self):
        s = self.script
        base = "/proj"
        old_isfile = s.os.path.isfile
        try:
            # only .env
            s.os.path.isfile = lambda p: p == f"{base}/.env"
            self.assertEqual(s.detect_env_file(base), f"{base}/.env")

            # only .env/env
            s.os.path.isfile = lambda p: p == f"{base}/.env/env"
            self.assertEqual(s.detect_env_file(base), f"{base}/.env/env")

            # both -> prefer .env
            s.os.path.isfile = lambda p: p in (f"{base}/.env", f"{base}/.env/env")
            self.assertEqual(s.detect_env_file(base), f"{base}/.env")

            # none
            s.os.path.isfile = lambda p: False
            self.assertIsNone(s.detect_env_file(base))
        finally:
            s.os.path.isfile = old_isfile

    def test_hard_restart_uses_envfile_and_cwd(self):
        s = self.script
        calls = []

        def fake_run(cmd, cwd=None, check=None):
            calls.append({"cmd": cmd, "cwd": cwd, "check": check})
            class R: pass
            return R()

        old_run = s.subprocess.run
        old_detect = s.detect_env_file
        try:
            s.subprocess.run = fake_run
            s.detect_env_file = lambda d: f"{d}/.env/env"  # erzwinge .env/env

            s.hard_restart_docker_services("/X/APP")

            # Wir erwarten zwei Aufrufe: docker-compose --env-file ... down / up -d
            self.assertEqual(len(calls), 2)
            self.assertEqual(calls[0]["cwd"], "/X/APP")
            self.assertEqual(calls[1]["cwd"], "/X/APP")
            # down
            self.assertIn("docker-compose", calls[0]["cmd"])
            self.assertIn("--env-file", calls[0]["cmd"])
            self.assertIn("/X/APP/.env/env", calls[0]["cmd"])
            self.assertIn("down", calls[0]["cmd"])
            # up -d
            self.assertIn("docker-compose", calls[1]["cmd"])
            self.assertIn("--env-file", calls[1]["cmd"])
            self.assertIn("/X/APP/.env/env", calls[1]["cmd"])
            self.assertIn("up", calls[1]["cmd"])
            self.assertIn("-d", calls[1]["cmd"])
        finally:
            s.subprocess.run = old_run
            s.detect_env_file = old_detect

    def test_main_scans_parent_and_filters_only(self):
        s = self.script
        seen = {"scandir": [], "called": []}

        class FakeDirEntry:
            def __init__(self, path, is_dir=True):
                self.path = path
                self._is_dir = is_dir
            def is_dir(self):
                return self._is_dir

        def fake_scandir(parent):
            seen["scandir"].append(parent)
            return [
                FakeDirEntry(f"{parent}/app1"),
                FakeDirEntry(f"{parent}/app2"),
                FakeDirEntry(f"{parent}/notdir", is_dir=False),
            ]

        def fake_isdir(p):
            return p == "/PARENT"

        def fake_isfile(p):
            # Nur app2 hat docker-compose.yml
            return p in ("/PARENT/app2/docker-compose.yml",)

        def fake_hard_restart(dir_path):
            seen["called"].append(dir_path)

        old_scandir = s.os.scandir
        old_isdir = s.os.path.isdir
        old_isfile = s.os.path.isfile
        old_restart = s.hard_restart_docker_services
        try:
            s.os.scandir = fake_scandir
            s.os.path.isdir = fake_isdir
            s.os.path.isfile = fake_isfile
            s.hard_restart_docker_services = fake_hard_restart

            # Mit --only app2 -> nur app2 wird aufgerufen
            sys_argv = sys.argv
            sys.argv = ["x", "/PARENT", "--only", "app2"]
            s.main()
            self.assertEqual(seen["called"], ["/PARENT/app2"])
        finally:
            s.os.scandir = old_scandir
            s.os.path.isdir = old_isdir
            s.os.path.isfile = old_isfile
            s.hard_restart_docker_services = old_restart
            sys.argv = sys_argv


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