from __future__ import annotations

import importlib.util
import sys
from pathlib import Path
from types import SimpleNamespace


MODULE_NAME = "sandbox_provisioner_app_for_test"


def _find_module_path() -> Path:
    current = Path(__file__).resolve()
    for parent in current.parents:
        candidate = parent / "docker" / "sandbox_provisioner" / "app.py"
        if candidate.exists():
            return candidate
    raise FileNotFoundError("docker/sandbox_provisioner/app.py not found from test path")


MODULE_PATH = _find_module_path()


def _load_module():
    existing = sys.modules.get(MODULE_NAME)
    if existing is not None:
        return existing

    spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
    assert spec is not None
    assert spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    sys.modules[MODULE_NAME] = module
    spec.loader.exec_module(module)
    return module


def test_canonical_backend_name(monkeypatch):
    monkeypatch.setenv("PROVISIONER_BACKEND", "memory")
    module = _load_module()

    assert module.canonical_backend_name("docker") == "docker"
    assert module.canonical_backend_name("kubernetes") == "kubernetes"


def test_merged_sandbox_env_user_values_override_global(monkeypatch):
    monkeypatch.setenv("PROVISIONER_BACKEND", "memory")
    module = _load_module()

    assert module.merged_sandbox_env(
        {"SHARED": "global", "GLOBAL_ONLY": "value"},
        {"SHARED": "user", "USER_ONLY": "value"},
    ) == {
        "SHARED": "user",
        "GLOBAL_ONLY": "value",
        "USER_ONLY": "value",
    }


def test_normalize_env_converts_values_to_strings(monkeypatch):
    monkeypatch.setenv("PROVISIONER_BACKEND", "memory")
    module = _load_module()

    assert module.normalize_env({"A": 1, "B": None, "": "ignored"}) == {"A": "1", "B": ""}


def test_memory_backend_accepts_split_thread_ids(monkeypatch):
    monkeypatch.setenv("PROVISIONER_BACKEND", "memory")
    module = _load_module()
    backend = module.MemoryProvisionerBackend()

    record = backend.create(
        "sandbox-1",
        "child-thread",
        "user-1",
        file_thread_id="parent-thread",
        skills_thread_id="child-skills-thread",
    )

    assert record.sandbox_id == "sandbox-1"
    assert backend.discover("sandbox-1") is record


def test_docker_mount_checks_use_file_and_skills_thread_ids(monkeypatch, tmp_path):
    monkeypatch.setenv("PROVISIONER_BACKEND", "memory")
    module = _load_module()
    backend = object.__new__(module.LocalContainerProvisionerBackend)
    backend._threads_host_path = str(tmp_path)

    workspace = tmp_path / "shared" / "user-1" / "workspace"
    uploads = tmp_path / "parent-thread" / "user-data" / "uploads"
    outputs = tmp_path / "parent-thread" / "user-data" / "outputs"
    skills = tmp_path / "child-skills-thread" / "skills"
    container = SimpleNamespace(
        attrs={
            "Mounts": [
                {"Destination": "/home/gem/user-data/workspace", "Source": str(workspace)},
                {"Destination": "/home/gem/user-data/uploads", "Source": str(uploads)},
                {"Destination": "/home/gem/user-data/outputs", "Source": str(outputs)},
                {"Destination": "/home/gem/skills", "Source": str(skills)},
            ]
        }
    )

    assert backend._has_expected_user_data_mounts(container, "parent-thread", "user-1") is True
    assert backend._is_expected_skills_mount(container, "child-skills-thread") is True
    assert backend._has_expected_user_data_mounts(container, "child-thread", "user-1") is False
    assert backend._is_expected_skills_mount(container, "parent-thread") is False


def test_kubernetes_mount_check_uses_file_and_skills_thread_ids(monkeypatch):
    monkeypatch.setenv("PROVISIONER_BACKEND", "memory")
    module = _load_module()
    pod = SimpleNamespace(
        spec=SimpleNamespace(
            containers=[
                SimpleNamespace(
                    name="sandbox",
                    volume_mounts=[
                        SimpleNamespace(
                            mount_path="/home/gem/user-data/workspace",
                            sub_path="threads/shared/user-1/workspace",
                        ),
                        SimpleNamespace(
                            mount_path="/home/gem/user-data/uploads",
                            sub_path="threads/parent-thread/user-data/uploads",
                        ),
                        SimpleNamespace(
                            mount_path="/home/gem/user-data/outputs",
                            sub_path="threads/parent-thread/user-data/outputs",
                        ),
                        SimpleNamespace(mount_path="/home/gem/skills", sub_path="threads/child-skills-thread/skills"),
                    ],
                )
            ]
        )
    )

    assert module.KubernetesProvisionerBackend._pod_has_expected_mounts(
        pod,
        file_thread_id="parent-thread",
        skills_thread_id="child-skills-thread",
        uid="user-1",
    )
    assert not module.KubernetesProvisionerBackend._pod_has_expected_mounts(
        pod,
        file_thread_id="child-thread",
        skills_thread_id="child-skills-thread",
        uid="user-1",
    )
