"""Edge-case tests for per-venv and global session config.

Covers scenarios not in test_config.py:
- Corrupted venv config
- clear_remote_config via VIRTUAL_ENV (no explicit venv_path arg)
- save_remote_config without venv_path or use_global (warning path)
- Uninstall flow: restore_venv deletes marker, then clear_remote_config
- Wrapper template includes VIRTUAL_ENV export
- clear_remote_config with non-existent venv path
- No cross-contamination between scopes
"""

from __future__ import annotations

import json
import stat
from pathlib import Path

import pytest

from specter_cli.config import (
    clear_remote_config,
    load_remote_config,
    save_remote_config,
)


@pytest.fixture(autouse=True)
def _isolated_config(tmp_path, monkeypatch):
    """Redirect all config files to a temp directory via SPECTER_HOME."""
    sd = tmp_path / ".specter"
    sd.mkdir()
    monkeypatch.setenv("SPECTER_HOME", str(sd))


class TestPerVenvEdgeCases:
    """Edge cases for per-venv config resolution."""

    def test_corrupted_venv_config_returns_empty(self, tmp_path, monkeypatch):
        """Corrupted .specter.json in venv should return empty (no fallback)."""
        venv = tmp_path / "myvenv"
        venv.mkdir()
        venv_cfg = venv / ".specter.json"
        venv_cfg.write_text("not valid json{{{")

        monkeypatch.setenv("VIRTUAL_ENV", str(venv))
        config = load_remote_config()
        assert config == {}

    def test_clear_via_virtual_env_not_explicit_path(self, tmp_path, monkeypatch):
        """clear_remote_config without venv_path uses VIRTUAL_ENV."""
        venv = tmp_path / "myvenv"
        venv.mkdir()

        venv_cfg = venv / ".specter.json"
        venv_cfg.write_text(
            json.dumps(
                {
                    "patched": True,
                    "gpu_type": "h100",
                    "host": "10.0.0.1",
                    "user": "root",
                    "session_id": "sess_abc",
                    "ssh_port": 22,
                }
            )
        )

        monkeypatch.setenv("VIRTUAL_ENV", str(venv))
        # Call without explicit venv_path — should use VIRTUAL_ENV.
        clear_remote_config()

        # Venv config should keep marker, lose session.
        venv_data = json.loads(venv_cfg.read_text())
        assert venv_data.get("patched") is True
        assert "session_id" not in venv_data
        assert "host" not in venv_data

    def test_clear_after_restore_venv_deletes_marker(self, tmp_path, monkeypatch):
        """Simulates uninstall flow: restore_venv deletes marker, then
        clear_remote_config runs. Should not crash."""
        venv = tmp_path / "myvenv"
        venv.mkdir()

        venv_cfg = venv / ".specter.json"
        venv_cfg.write_text(
            json.dumps(
                {
                    "patched": True,
                    "gpu_type": "h100",
                    "host": "10.0.0.1",
                    "session_id": "sess_abc",
                }
            )
        )

        # Simulate restore_venv deleting the marker.
        venv_cfg.unlink()

        # clear_remote_config should not crash.
        clear_remote_config(venv_path=str(venv))
        assert not venv_cfg.exists()

    def test_clear_with_nonexistent_venv_path(self, tmp_path, monkeypatch):
        """clear_remote_config with a venv path that doesn't exist should
        not crash."""
        fake_venv = tmp_path / "nonexistent_venv"
        clear_remote_config(venv_path=str(fake_venv))
        # Should complete without error.

    def test_venv_config_missing_host_returns_empty(self, tmp_path, monkeypatch):
        """Venv config with session_id but no host returns empty."""
        venv = tmp_path / "myvenv"
        venv.mkdir()

        venv_cfg = venv / ".specter.json"
        venv_cfg.write_text(
            json.dumps(
                {
                    "patched": True,
                    "gpu_type": "h100",
                    "session_id": "sess_partial",
                    # Missing host.
                }
            )
        )

        monkeypatch.setenv("VIRTUAL_ENV", str(venv))
        config = load_remote_config()
        # Should return empty because host is missing.
        assert config == {}

    def test_venv_config_missing_session_id_returns_empty(self, tmp_path, monkeypatch):
        """Venv config with host but no session_id returns empty."""
        venv = tmp_path / "myvenv"
        venv.mkdir()

        venv_cfg = venv / ".specter.json"
        venv_cfg.write_text(
            json.dumps(
                {
                    "patched": True,
                    "gpu_type": "h100",
                    "host": "10.0.0.2",
                    # Missing session_id.
                }
            )
        )

        monkeypatch.setenv("VIRTUAL_ENV", str(venv))
        config = load_remote_config()
        assert config == {}

    def test_no_cross_contamination_between_scopes(self, tmp_path, monkeypatch):
        """Venv and global scopes are completely independent."""
        venv = tmp_path / "myvenv"
        venv.mkdir()

        # Install in venv scope.
        save_remote_config(
            host="10.0.0.1",
            user="root",
            session_id="sess_venv",
            gpu_type="h100",
            venv_path=str(venv),
        )

        # Install in global scope.
        save_remote_config(
            host="10.0.0.2",
            user="root",
            session_id="sess_global",
            gpu_type="a100",
            use_global=True,
        )

        # Activate venv — venv scope returns venv data.
        monkeypatch.setenv("VIRTUAL_ENV", str(venv))
        assert load_remote_config()["session_id"] == "sess_venv"
        assert load_remote_config(use_global=True)["session_id"] == "sess_global"

        # Clear venv — global unaffected.
        clear_remote_config(venv_path=str(venv))
        assert load_remote_config() == {}
        assert load_remote_config(use_global=True)["session_id"] == "sess_global"

        # Clear global — venv already cleared.
        clear_remote_config(use_global=True)
        assert load_remote_config(use_global=True) == {}


class TestWrapperVirtualEnvExport:
    """Verify the wrapper template includes VIRTUAL_ENV export."""

    def _make_standard_venv(self, tmp_path: Path) -> Path:
        """Create a fake venv with the expected structure."""
        venv = tmp_path / "myvenv"
        bin_dir = venv / "bin"
        bin_dir.mkdir(parents=True)

        python_bin = bin_dir / "python"
        python_bin.write_text("#!/usr/bin/env python3\n# real python\n")
        python_bin.chmod(python_bin.stat().st_mode | stat.S_IEXEC)

        python3_bin = bin_dir / "python3"
        python3_bin.symlink_to("python")

        return venv

    def test_wrapper_exports_virtual_env(self, tmp_path):
        """Wrapper should export VIRTUAL_ENV for non-activated invocations."""
        from specter_cli.venv import patch_venv

        venv = self._make_standard_venv(tmp_path)
        patch_venv(venv, "h100")

        wrapper = (venv / "bin" / "python").read_text()
        assert "export VIRTUAL_ENV=" in wrapper
        # Must use fallback: ${VIRTUAL_ENV:-...} to not override activated venvs.
        assert "VIRTUAL_ENV:-" in wrapper

    def test_wrapper_fallback_resolves_to_venv_root(self, tmp_path):
        """VIRTUAL_ENV fallback should resolve to the venv root directory."""
        from specter_cli.venv import patch_venv

        venv = self._make_standard_venv(tmp_path)
        patch_venv(venv, "h100")

        wrapper = (venv / "bin" / "python").read_text()
        # The fallback computes the parent of bin/: $(cd "$(dirname "$0")/.." && pwd)
        assert '"$(dirname "$0")/.."' in wrapper

    def test_wrapper_does_not_override_activated_venv(self, tmp_path):
        """When VIRTUAL_ENV is already set, wrapper should not override it."""
        from specter_cli.venv import patch_venv

        venv = self._make_standard_venv(tmp_path)
        patch_venv(venv, "h100")

        wrapper = (venv / "bin" / "python").read_text()
        # ${VIRTUAL_ENV:-fallback} means: use VIRTUAL_ENV if set, else fallback.
        assert "${VIRTUAL_ENV:-" in wrapper
