"""
Tests for Phase 88: Network Launcher — Launch Swarm V2 on the Network.

Covers: initialization, subsystem registration, health checks, shutdown,
diagnostics, stats, edge cases, and error handling.
"""

import json
import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    """Redirect all network storage to tmp_path and reset in-memory state."""
    network_dir = tmp_path / "network"
    network_dir.mkdir(parents=True, exist_ok=True)

    import brynq.network_launcher as nl
    monkeypatch.setattr(nl, "NETWORK_DIR", network_dir)
    monkeypatch.setattr(nl, "STATUS_FILE", network_dir / "status.json")
    monkeypatch.setattr(nl, "EVENTS_FILE", network_dir / "events.jsonl")
    monkeypatch.setattr(nl, "BROKER_DIR", tmp_path)
    nl.reset_state()


# ── 1. Initialization ───────────────────────────────────────────────────


class TestInitializeNetwork:
    def test_basic_init(self):
        from brynq.network_launcher import initialize_network
        result = initialize_network()
        assert result["status"] == "initialized"
        assert len(result["subsystems_registered"]) >= 1
        assert result["started_at"] is not None

    def test_init_with_config(self):
        import brynq.network_launcher as nl
        result = nl.initialize_network(config={"region": "us-east"})
        assert nl._network_state["config"]["region"] == "us-east"

    def test_init_with_custom_subsystems(self):
        from brynq.network_launcher import initialize_network
        result = initialize_network(config={"subsystems": ["custom_a", "custom_b"]})
        assert "custom_a" in result["subsystems_registered"]
        assert "custom_b" in result["subsystems_registered"]

    def test_init_writes_status_file(self):
        from brynq.network_launcher import initialize_network, STATUS_FILE
        initialize_network()
        assert STATUS_FILE.exists()
        data = json.loads(STATUS_FILE.read_text("utf-8"))
        assert data["state"] == "running"


# ── 2. Subsystem Registration ───────────────────────────────────────────


class TestRegisterSubsystem:
    def test_register_basic(self):
        from brynq.network_launcher import register_subsystem
        result = register_subsystem("test_sub", lambda: {"healthy": True, "detail": "ok"})
        assert result["registered"] == "test_sub"
        assert result["total_subsystems"] == 1

    def test_register_multiple(self):
        from brynq.network_launcher import register_subsystem
        register_subsystem("sub_a", lambda: {"healthy": True, "detail": "ok"})
        result = register_subsystem("sub_b", lambda: {"healthy": True, "detail": "ok"})
        assert result["total_subsystems"] == 2

    def test_register_overwrites(self):
        from brynq.network_launcher import register_subsystem, _subsystems
        register_subsystem("sub_c", lambda: {"healthy": True, "detail": "v1"})
        register_subsystem("sub_c", lambda: {"healthy": False, "detail": "v2"})
        check = _subsystems["sub_c"]["health_check_fn"]()
        assert check["detail"] == "v2"


# ── 3. Health Checks ────────────────────────────────────────────────────


class TestHealthChecks:
    def test_check_all_healthy(self):
        from brynq.network_launcher import initialize_network, check_all_subsystems
        initialize_network()
        result = check_all_subsystems()
        assert result["healthy"] == result["checked"]
        assert result["unhealthy"] == 0

    def test_check_with_unhealthy(self):
        from brynq.network_launcher import (register_subsystem,
                                             check_all_subsystems)
        register_subsystem("good", lambda: {"healthy": True, "detail": "ok"})
        register_subsystem("bad", lambda: {"healthy": False, "detail": "down"})
        result = check_all_subsystems()
        assert result["healthy"] == 1
        assert result["unhealthy"] == 1

    def test_check_with_exception(self):
        from brynq.network_launcher import register_subsystem, check_all_subsystems
        def failing_check():
            raise RuntimeError("boom")
        register_subsystem("crasher", failing_check)
        result = check_all_subsystems()
        assert result["unhealthy"] == 1
        assert "boom" in result["results"]["crasher"]["detail"]

    def test_get_subsystem_status(self):
        from brynq.network_launcher import register_subsystem, get_subsystem_status
        register_subsystem("my_sub", lambda: {"healthy": True, "detail": "running"})
        status = get_subsystem_status("my_sub")
        assert status["name"] == "my_sub"
        assert status["healthy"] is True

    def test_get_subsystem_not_found(self):
        from brynq.network_launcher import get_subsystem_status
        result = get_subsystem_status("nonexistent")
        assert "error" in result

    def test_get_subsystem_exception(self):
        from brynq.network_launcher import register_subsystem, get_subsystem_status
        register_subsystem("bad_sub", lambda: (_ for _ in ()).throw(ValueError("oops")))
        result = get_subsystem_status("bad_sub")
        assert result["healthy"] is False


# ── 4. Network Status ───────────────────────────────────────────────────


class TestNetworkStatus:
    def test_status_not_initialized(self):
        from brynq.network_launcher import get_network_status
        result = get_network_status()
        assert result["status"] == "not_initialized"

    def test_status_after_init(self):
        from brynq.network_launcher import initialize_network, get_network_status
        initialize_network()
        status = get_network_status()
        assert status["status"] == "running"
        assert status["health_score"] == 100.0
        assert status["total_subsystems"] >= 1

    def test_status_health_score(self):
        from brynq.network_launcher import (initialize_network,
                                             register_subsystem,
                                             get_network_status)
        initialize_network(config={"subsystems": []})
        register_subsystem("ok1", lambda: {"healthy": True, "detail": "ok"})
        register_subsystem("ok2", lambda: {"healthy": True, "detail": "ok"})
        register_subsystem("fail1", lambda: {"healthy": False, "detail": "bad"})
        status = get_network_status()
        # 2 healthy out of 3 = 66.7%
        assert 66 <= status["health_score"] <= 67


# ── 5. Shutdown ──────────────────────────────────────────────────────────


class TestShutdown:
    def test_graceful_shutdown(self):
        from brynq.network_launcher import initialize_network, shutdown_network
        initialize_network()
        result = shutdown_network(reason="maintenance")
        assert result["status"] == "shutdown"
        assert result["reason"] == "maintenance"
        assert len(result["subsystems_stopped"]) >= 1

    def test_shutdown_clears_state(self):
        from brynq.network_launcher import (initialize_network,
                                             shutdown_network,
                                             get_network_status)
        initialize_network()
        shutdown_network()
        status = get_network_status()
        assert status["status"] == "not_initialized"

    def test_shutdown_when_not_running(self):
        from brynq.network_launcher import shutdown_network
        result = shutdown_network()
        assert result["status"] == "already_stopped"

    def test_shutdown_writes_status(self):
        from brynq.network_launcher import (initialize_network,
                                             shutdown_network, STATUS_FILE)
        initialize_network()
        shutdown_network(reason="test")
        data = json.loads(STATUS_FILE.read_text("utf-8"))
        assert data["state"] == "stopped"
        assert data["reason"] == "test"


# ── 6. Network Stats ────────────────────────────────────────────────────


class TestNetworkStats:
    def test_stats_not_initialized(self):
        from brynq.network_launcher import get_network_stats
        stats = get_network_stats()
        assert stats["initialized"] is False
        assert stats["modules_loaded"] == 0

    def test_stats_after_init(self):
        from brynq.network_launcher import initialize_network, get_network_stats
        initialize_network()
        stats = get_network_stats()
        assert stats["initialized"] is True
        assert stats["modules_loaded"] >= 1
        assert stats["events_processed"] >= 1  # at least the init event
        assert stats["uptime_seconds"] >= 0

    def test_stats_subsystems_list(self):
        from brynq.network_launcher import (initialize_network,
                                             register_subsystem,
                                             get_network_stats)
        initialize_network(config={"subsystems": []})
        register_subsystem("custom", lambda: {"healthy": True, "detail": "ok"})
        stats = get_network_stats()
        assert "custom" in stats["subsystems"]


# ── 7. Startup Diagnostics ──────────────────────────────────────────────


class TestDiagnostics:
    def test_diagnostics_pass(self):
        from brynq.network_launcher import run_startup_diagnostics
        result = run_startup_diagnostics()
        assert result["overall"] == "pass"
        assert result["modules"]["brynq.server"] == "ok"

    def test_diagnostics_checks_directories(self):
        from brynq.network_launcher import run_startup_diagnostics
        result = run_startup_diagnostics()
        assert result["directories"]["network_dir"] == "ok"

    def test_diagnostics_config_not_initialized(self):
        from brynq.network_launcher import run_startup_diagnostics
        result = run_startup_diagnostics()
        assert result["config_status"] == "not_initialized"

    def test_diagnostics_config_loaded(self):
        from brynq.network_launcher import initialize_network, run_startup_diagnostics
        initialize_network()
        result = run_startup_diagnostics()
        assert result["config_status"] == "loaded"

    def test_diagnostics_missing_dir(self, tmp_path, monkeypatch):
        import brynq.network_launcher as nl
        monkeypatch.setattr(nl, "BROKER_DIR", tmp_path / "nonexistent")
        result = nl.run_startup_diagnostics()
        assert result["directories"]["broker_dir"] == "MISSING"
        assert result["overall"] == "fail"


# ── 8. Event Logging ────────────────────────────────────────────────────


class TestEvents:
    def test_init_logs_event(self):
        from brynq.network_launcher import initialize_network, EVENTS_FILE
        initialize_network()
        from brynq.server import _read_jsonl_sync
        events = _read_jsonl_sync(EVENTS_FILE)
        init_events = [e for e in events if e.get("event") == "network_initialized"]
        assert len(init_events) >= 1

    def test_shutdown_logs_event(self):
        from brynq.network_launcher import (initialize_network,
                                             shutdown_network, EVENTS_FILE)
        initialize_network()
        shutdown_network(reason="test")
        from brynq.server import _read_jsonl_sync
        events = _read_jsonl_sync(EVENTS_FILE)
        shutdown_events = [e for e in events if e.get("event") == "network_shutdown"]
        assert len(shutdown_events) >= 1
        assert shutdown_events[0]["reason"] == "test"

    def test_diagnostics_logs_event(self):
        from brynq.network_launcher import run_startup_diagnostics, EVENTS_FILE
        run_startup_diagnostics()
        from brynq.server import _read_jsonl_sync
        events = _read_jsonl_sync(EVENTS_FILE)
        diag_events = [e for e in events if e.get("event") == "diagnostics_run"]
        assert len(diag_events) >= 1
