import importlib.util
import json
import re
from pathlib import Path

from core.install_bootstrap import get_install_status


ROOT = Path(__file__).resolve().parents[1]
CATALOG_PATH = ROOT / "config" / "relay_image_catalog.json"
GENERATOR_PATH = ROOT / "scripts" / "generate-relay-image.py"


def _load_generator():
    spec = importlib.util.spec_from_file_location("generate_relay_image", GENERATOR_PATH)
    module = importlib.util.module_from_spec(spec)
    assert spec.loader is not None
    spec.loader.exec_module(module)
    return module


def _catalog():
    return json.loads(CATALOG_PATH.read_text(encoding="utf-8"))


def test_relay_catalog_has_required_base_runtime():
    catalog = _catalog()
    base = catalog["features"]["relay.base"]

    assert re.fullmatch(r"\d{4}\.\d{2}\.\d{2}", catalog["relay_image_version"])
    assert "relay.base" in catalog["required_features"]
    assert base["required"] is True
    assert "python3" in base["apt"]
    assert "python3-dev" in base["apt"]
    assert "vim-tiny" in base["apt"]
    assert "nano" in base["apt"]
    assert "procps" in base["apt"]
    assert "iproute2" in base["apt"]
    assert "netcat-openbsd" in base["apt"]
    assert "fuse3" in base["apt"]
    assert "libfuse3-dev" in base["apt"]
    assert "build-essential" in base["apt"]
    assert "pkg-config" in base["apt"]
    assert "pyfuse3" in base["pip"]
    assert "trio" in base["pip"]
    post_install = "\n".join(base["post_install"])
    assert "/workspace" in post_install
    assert "/cc_sessions" in post_install
    assert "/filestore" in post_install
    assert 'chown -R pawflow:pawflow "$d"' in post_install
    assert "chmod g+rwx" in post_install
    assert "sudo -E -u pawflow" in post_install
    assert base["runtime"]["requires_fuse"] is True
    assert "/dev/fuse" in base["runtime"]["docker_args"]


def test_gui_apps_are_individually_selectable_and_imply_desktop_runtime():
    catalog = _catalog()
    gui_features = {
        feature_id: feature
        for feature_id, feature in catalog["features"].items()
        if feature.get("category") == "gui_apps"
    }

    assert "gui.gimp" in gui_features
    assert "gui.inkscape" in gui_features
    assert "gui.vscode" not in gui_features
    assert "gui.libreoffice-calc" in gui_features
    assert "gui.audacity" in gui_features
    assert len(gui_features) >= 10
    for feature in gui_features.values():
        assert "desktop.runtime" in feature.get("implies", [])


def test_server_profile_is_full_and_execution_profile_is_minimal():
    catalog = _catalog()
    server_features = set(catalog["profiles"][catalog["server_profile"]]["features"])
    server_minimal_features = set(catalog["profiles"][catalog["server_minimal_profile"]]["features"])
    client_features = set(catalog["profiles"][catalog["default_client_profile"]]["features"])

    assert catalog["server_profile"] == "server-full"
    assert catalog["server_minimal_profile"] == "server-minimal"
    assert catalog["default_client_profile"] == "client-minimal"
    assert server_minimal_features == {"relay.base"}
    assert client_features == {"relay.base"}
    for required in ("lang.python-dev", "lang.node", "lang.rust", "lang.java", "desktop.runtime", "browser.chromium", "gui.gimp"):
        assert required in server_features

    for omitted in ("lang.dotnet", "lang.zig", "gui.libreoffice-calc", "gui.vlc", "gui.audacity"):
        assert omitted not in server_features
    assert "lang.java-kotlin" not in server_features
    assert "lang.java" in _catalog()["features"]["lang.java-kotlin"].get("implies", [])
    assert "browser.chrome" not in server_features
    assert "gui.vscode" not in server_features


def test_generator_resolves_implied_features_and_writes_installer_artifacts(tmp_path):
    generator = _load_generator()
    out_dir = tmp_path / "relay"

    manifest = generator.generate(
        CATALOG_PATH,
        "client-minimal",
        ["gui.gimp", "lang.node", "browser.chromium"],
        out_dir,
        "pawflow-relay:test",
    )

    assert (out_dir / "Dockerfile").exists()
    assert (out_dir / "manifest.json").exists()
    assert (out_dir / "build.sh").exists()
    assert (out_dir / "run-relay.sh").exists()
    assert (out_dir / "runtime" / "pawflow_relay_launcher.py").exists()
    assert (out_dir / "runtime" / "fs_actions.py").exists()
    assert (out_dir / "runtime" / "screen_actions.py").exists()
    assert (out_dir / "runtime" / "pawflow.py").exists()
    assert (out_dir / "runtime" / "pawflow_relay" / "__init__.py").exists()
    assert "relay.base" in manifest["features"]
    assert manifest["relay_image_version"] == _catalog()["relay_image_version"]
    assert "gui.gimp" in manifest["features"]
    assert "desktop.runtime" in manifest["features"]
    assert "lang.node" in manifest["features"]
    assert "browser.chromium" in manifest["features"]
    assert "/dev/fuse" in manifest["runtime_docker_args"]

    dockerfile = (out_dir / "Dockerfile").read_text(encoding="utf-8")
    assert "python3 /opt/pawflow/pawflow_relay_launcher.py" not in dockerfile
    assert dockerfile.index("pkg-config") < dockerfile.index("pip3 install")
    assert dockerfile.index("libfuse3-dev") < dockerfile.index("pip3 install")
    assert "https://deb.nodesource.com/setup_22.x" in dockerfile
    assert dockerfile.index("https://deb.nodesource.com/setup_22.x") < dockerfile.index("nodejs")
    assert "gimp gimp-plugin-registry" in dockerfile
    assert "software-properties-common" in dockerfile
    assert "ppa:xtradeb/apps" in dockerfile
    assert "apt-get install -y --no-install-recommends" in dockerfile
    assert "chromium" in dockerfile
    assert "/usr/bin/chromium --no-sandbox" in dockerfile
    assert 'profile_dir="$HOME/.chromium-profile"' in dockerfile
    assert "--disk-cache-dir=\"$cache_dir\"" in dockerfile
    assert "rm -f \"$profile_dir/SingletonLock\"" in dockerfile
    assert "\"$profile_dir/SingletonSocket\"" in dockerfile
    assert "\"$profile_dir/SingletonCookie\"" in dockerfile
    assert "update-alternatives --set x-www-browser /usr/local/bin/chromium" in dockerfile
    assert "WebBrowser=chromium" in dockerfile
    assert "X-XFCE-CommandsWithParameter=/usr/local/bin/chromium" in dockerfile
    assert "x-scheme-handler/https=chromium.desktop" in dockerfile
    assert "/ms-playwright" not in dockerfile
    assert "google-chrome" not in dockerfile
    assert "packages.microsoft.com/repos/code" not in dockerfile
    assert "COPY runtime/ /opt/pawflow/" not in dockerfile
    assert "USER root" in dockerfile
    assert "sudo -E -u pawflow" in dockerfile
    assert 'chown -R pawflow:pawflow "$d"' in dockerfile
    assert "chmod g+rwx" in dockerfile
    assert "XDG_CACHE_HOME=\"/tmp/pawflow-cache\"" in dockerfile
    assert "HF_HOME=\"/tmp/pawflow-cache/huggingface\"" in dockerfile

    run_script = (out_dir / "run-relay.sh").read_text(encoding="utf-8")
    assert "PAWFLOW_RELAY_TOKEN" in run_script
    assert '"$SCRIPT_DIR/runtime:/opt/pawflow:ro"' in run_script
    assert "--server-mount /cc_sessions" in run_script
    assert "--filestore-mount /filestore" in run_script
    assert "--device /dev/fuse" in run_script

    build_script = (out_dir / "build.sh").read_text(encoding="utf-8")
    assert "SCRIPT_DIR=" in build_script
    assert "PAWFLOW_DOCKER_PLATFORM" in build_script
    assert 'docker build "${BUILD_ARGS[@]}" -t "$IMAGE" "$SCRIPT_DIR"' in build_script


def test_server_minimal_build_script_targets_runtime_default_image():
    script = ROOT / "scripts" / "build-server-minimal-relay.sh"
    src = script.read_text(encoding="utf-8")

    assert script.exists()
    assert script.stat().st_mode & 0o111
    assert "set -euo pipefail" in src
    assert "--profile server-minimal" in src
    assert "pawflow-relay-minimal:latest" in src
    assert "docker/relay-generated/server-minimal" in src
    assert "PAWFLOW_SERVER_MINIMAL_RELAY_IMAGE" in src


def test_installer_api_advertises_relay_image_profile_step():
    flow = json.loads((ROOT / "data/repository/flows/global/default/pawflow_installer/versions/1.0.0.json").read_text(encoding="utf-8"))
    api_content = get_install_status()

    assert flow["tasks"]["install_api"]["type"] == "installBootstrap"
    assert "relay_image_profiles" in api_content["steps"]
    assert api_content["client_relay_images"]["catalog"] == "config/relay_image_catalog.json"
    assert api_content["client_relay_images"]["server_profile"] == "server-full"
    assert api_content["client_relay_images"]["server_minimal_profile"] == "server-minimal"
    assert api_content["client_relay_images"]["advanced_features"] is True
    ui_asset = ROOT / "data/repository/flows/global/default/pawflow_installer/versions/assets/install.html"
    assert "Relay image profiles" in ui_asset.read_text(encoding="utf-8")
