import json
from pathlib import Path

import pytest

from armorer.registry import AppMetadata, AppRegistry


def _write_registry(path: Path, payload: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(payload, indent=2))


def test_registry_init_tolerates_best_effort_sync_save_failure(tmp_path: Path, monkeypatch):
    registry_path = tmp_path / ".armorer" / "registry.json"
    install_dir = tmp_path / "installed" / "openclaw"
    install_dir.mkdir(parents=True)
    _write_registry(
        registry_path,
        {
            "openclaw": {
                "name": "openclaw",
                "install_path": str(install_dir),
                "agent_id": "openclaw",
                "monitor_targets": [],
                "monitoring_mode": "hybrid",
            }
        },
    )
    monkeypatch.setattr(AppRegistry, "REGISTRY_FILE", registry_path)

    save_attempts: list[dict] = []

    def _boom(self, data):
        save_attempts.append(data)
        raise PermissionError("read-only registry")

    monkeypatch.setattr(AppRegistry, "_save_registry", _boom, raising=True)

    registry = AppRegistry()

    app = registry.get_app("openclaw")

    assert app is not None
    assert app.install_path == str(install_dir)
    assert save_attempts


def test_registry_get_app_normalizes_path_when_best_effort_save_fails(tmp_path: Path, monkeypatch):
    registry_path = tmp_path / ".armorer" / "registry.json"
    install_dir = tmp_path / "installed" / "openclaw"
    install_dir.mkdir(parents=True)
    _write_registry(
        registry_path,
        {
            "openclaw": {
                "name": "openclaw",
                "install_path": "/app/installed/openclaw",
                "agent_id": "openclaw",
                "monitor_targets": ["sessions/**/*.json"],
                "monitoring_mode": "hybrid",
            }
        },
    )
    monkeypatch.chdir(tmp_path)
    monkeypatch.setattr(AppRegistry, "REGISTRY_FILE", registry_path)
    monkeypatch.setattr(AppRegistry, "_sync_known_monitor_targets", lambda self: None, raising=True)

    save_attempts: list[dict] = []

    def _boom(self, data):
        save_attempts.append(data)
        raise PermissionError("read-only registry")

    monkeypatch.setattr(AppRegistry, "_save_registry", _boom, raising=True)

    registry = AppRegistry()

    app = registry.get_app("openclaw")

    assert app is not None
    assert app.install_path == str(install_dir)
    assert len(save_attempts) == 1
    stored = json.loads(registry_path.read_text())
    assert stored["openclaw"]["install_path"] == "/app/installed/openclaw"


def test_registry_list_apps_normalizes_paths_when_best_effort_save_fails(tmp_path: Path, monkeypatch):
    registry_path = tmp_path / ".armorer" / "registry.json"
    install_dir = tmp_path / "installed" / "openclaw"
    install_dir.mkdir(parents=True)
    _write_registry(
        registry_path,
        {
            "openclaw": {
                "name": "openclaw",
                "install_path": "/app/installed/openclaw",
                "agent_id": "openclaw",
                "monitor_targets": ["sessions/**/*.json"],
                "monitoring_mode": "hybrid",
            }
        },
    )
    monkeypatch.chdir(tmp_path)
    monkeypatch.setattr(AppRegistry, "REGISTRY_FILE", registry_path)
    monkeypatch.setattr(AppRegistry, "_sync_known_monitor_targets", lambda self: None, raising=True)
    monkeypatch.setattr(
        AppRegistry,
        "_save_registry",
        lambda self, data: (_ for _ in ()).throw(PermissionError("read-only registry")),
        raising=True,
    )

    registry = AppRegistry()

    apps = registry.list_apps()

    assert [app.name for app in apps] == ["openclaw"]
    assert apps[0].install_path == str(install_dir)


def test_registry_add_app_still_raises_when_save_fails(tmp_path: Path, monkeypatch):
    registry_path = tmp_path / ".armorer" / "registry.json"
    _write_registry(registry_path, {})
    monkeypatch.setattr(AppRegistry, "REGISTRY_FILE", registry_path)
    monkeypatch.setattr(AppRegistry, "_sync_known_monitor_targets", lambda self: None, raising=True)
    monkeypatch.setattr(
        AppRegistry,
        "_save_registry",
        lambda self, data: (_ for _ in ()).throw(PermissionError("read-only registry")),
        raising=True,
    )

    registry = AppRegistry()

    with pytest.raises(PermissionError):
        registry.add_app(AppMetadata(name="openclaw", install_path=str(tmp_path / "installed" / "openclaw")))


def test_registry_uses_config_dir_env_override(tmp_path: Path, monkeypatch):
    env_config = tmp_path / "alt-armorer"
    monkeypatch.setenv("CONFIG_DIR", str(env_config))

    registry = AppRegistry()
    registry.add_app(AppMetadata(name="openclaw", install_path=str(tmp_path / "isolated" / "openclaw")))

    assert registry.REGISTRY_FILE == env_config / "registry.json"
    assert registry.REGISTRY_FILE.exists()
    assert json.loads(registry.REGISTRY_FILE.read_text())["openclaw"]["name"] == "openclaw"


def test_registry_prefers_armorer_config_dir_env_over_legacy_config_dir(tmp_path: Path, monkeypatch):
    armorer_config = tmp_path / "armorer-env"
    legacy_config = tmp_path / "legacy-env"
    monkeypatch.setenv("ARMORER_CONFIG_DIR", str(armorer_config))
    monkeypatch.setenv("CONFIG_DIR", str(legacy_config))

    registry = AppRegistry()
    registry.add_app(AppMetadata(name="openclaw", install_path=str(tmp_path / "isolated" / "openclaw")))

    assert registry.REGISTRY_FILE == armorer_config / "registry.json"
    assert registry.REGISTRY_FILE.exists()
    assert not (legacy_config / "registry.json").exists()
