"""Tests for Phase 72: Hierarchical Swarms (25+ tests)."""

import json
from pathlib import Path

import pytest

import brynq.server as srv
import brynq.hierarchical_swarms as hs


@pytest.fixture(autouse=True)
def isolate(tmp_path, monkeypatch):
    monkeypatch.setattr(srv, "BROKER_DIR", tmp_path)
    monkeypatch.setattr(hs, "HIERARCHY_DIR", tmp_path / "swarm_hierarchy")
    (tmp_path / "swarm_hierarchy").mkdir(parents=True, exist_ok=True)


# ── create_sub_swarm ─────────────────────────────────────────────────────


class TestCreateSubSwarm:
    def test_basic_create(self):
        sub = hs.create_sub_swarm("root", "frontend", "build UI", "agent-1")
        assert sub["name"] == "frontend"
        assert sub["task"] == "build UI"
        assert sub["lead_agent_id"] == "agent-1"
        assert sub["parent_swarm_id"] == "root"
        assert sub["status"] == "active"

    def test_parent_gets_child_registered(self):
        sub = hs.create_sub_swarm("root", "frontend", "build UI", "agent-1")
        parent = hs._load("root")
        assert sub["swarm_id"] in parent["sub_swarms"]

    def test_multiple_children(self):
        s1 = hs.create_sub_swarm("root", "frontend", "UI", "a1")
        s2 = hs.create_sub_swarm("root", "backend", "API", "a2")
        s3 = hs.create_sub_swarm("root", "tests", "QA", "a3")
        parent = hs._load("root")
        assert len(parent["sub_swarms"]) == 3

    def test_lead_added_to_agents(self):
        sub = hs.create_sub_swarm("root", "sub", "task", "lead-1")
        assert "lead-1" in sub["agents"]

    def test_sub_swarm_unique_id(self):
        s1 = hs.create_sub_swarm("root", "a", "t", "l")
        s2 = hs.create_sub_swarm("root", "b", "t", "l")
        assert s1["swarm_id"] != s2["swarm_id"]


# ── get_sub_swarms ───────────────────────────────────────────────────────


class TestGetSubSwarms:
    def test_no_children(self):
        hs._ensure_root("root")
        assert hs.get_sub_swarms("root") == []

    def test_returns_children(self):
        hs.create_sub_swarm("root", "a", "t1", "l1")
        hs.create_sub_swarm("root", "b", "t2", "l2")
        children = hs.get_sub_swarms("root")
        assert len(children) == 2
        names = {c["name"] for c in children}
        assert names == {"a", "b"}

    def test_nonexistent_parent(self):
        assert hs.get_sub_swarms("nonexistent") == []


# ── get_parent ───────────────────────────────────────────────────────────


class TestGetParent:
    def test_root_has_no_parent(self):
        hs._ensure_root("root")
        assert hs.get_parent("root") is None

    def test_child_returns_parent(self):
        sub = hs.create_sub_swarm("root", "child", "task", "lead")
        parent = hs.get_parent(sub["swarm_id"])
        assert parent is not None
        assert parent["swarm_id"] == "root"

    def test_nonexistent_swarm(self):
        assert hs.get_parent("ghost") is None


# ── get_hierarchy ────────────────────────────────────────────────────────


class TestGetHierarchy:
    def test_single_root(self):
        hs._ensure_root("root")
        tree = hs.get_hierarchy("root")
        assert tree["swarm_id"] == "root"
        assert tree["children"] == []

    def test_one_level(self):
        hs.create_sub_swarm("root", "a", "t", "l")
        hs.create_sub_swarm("root", "b", "t", "l")
        tree = hs.get_hierarchy("root")
        assert len(tree["children"]) == 2

    def test_nested_hierarchy(self):
        sub = hs.create_sub_swarm("root", "level1", "t", "l")
        hs.create_sub_swarm(sub["swarm_id"], "level2", "t", "l")
        tree = hs.get_hierarchy("root")
        assert len(tree["children"]) == 1
        assert len(tree["children"][0]["children"]) == 1

    def test_nonexistent(self):
        assert hs.get_hierarchy("ghost") == {}

    def test_deep_hierarchy(self):
        """Three levels deep."""
        s1 = hs.create_sub_swarm("root", "L1", "t", "l")
        s2 = hs.create_sub_swarm(s1["swarm_id"], "L2", "t", "l")
        s3 = hs.create_sub_swarm(s2["swarm_id"], "L3", "t", "l")
        tree = hs.get_hierarchy("root")
        l3 = tree["children"][0]["children"][0]["children"][0]
        assert l3["name"] == "L3"


# ── propagate_status ─────────────────────────────────────────────────────


class TestPropagateStatus:
    def test_all_children_complete(self):
        s1 = hs.create_sub_swarm("root", "a", "t", "l")
        s2 = hs.create_sub_swarm("root", "b", "t", "l")
        # Complete both children
        c1 = hs._load(s1["swarm_id"])
        c1["status"] = "completed"
        hs._save(c1)
        c2 = hs._load(s2["swarm_id"])
        c2["status"] = "completed"
        hs._save(c2)

        result = hs.propagate_status(s1["swarm_id"])
        assert result["propagated"] is True
        parent = hs._load("root")
        assert parent["status"] == "completed"

    def test_partial_completion(self):
        s1 = hs.create_sub_swarm("root", "a", "t", "l")
        s2 = hs.create_sub_swarm("root", "b", "t", "l")
        c1 = hs._load(s1["swarm_id"])
        c1["status"] = "completed"
        hs._save(c1)

        result = hs.propagate_status(s1["swarm_id"])
        parent = hs._load("root")
        assert parent["status"] == "active"  # not all done

    def test_failed_child(self):
        s1 = hs.create_sub_swarm("root", "a", "t", "l")
        s2 = hs.create_sub_swarm("root", "b", "t", "l")
        c1 = hs._load(s1["swarm_id"])
        c1["status"] = "failed"
        hs._save(c1)
        c2 = hs._load(s2["swarm_id"])
        c2["status"] = "completed"
        hs._save(c2)

        hs.propagate_status(s1["swarm_id"])
        parent = hs._load("root")
        assert parent["status"] == "failed"

    def test_root_no_parent(self):
        hs._ensure_root("root")
        result = hs.propagate_status("root")
        assert result["propagated"] is False

    def test_nonexistent(self):
        result = hs.propagate_status("ghost")
        assert "error" in result


# ── dissolve_sub_swarm ───────────────────────────────────────────────────


class TestDissolveSubSwarm:
    def test_dissolve(self):
        sub = hs.create_sub_swarm("root", "temp", "task", "lead")
        result = hs.dissolve_sub_swarm(sub["swarm_id"], reason="no longer needed")
        assert result["status"] == "dissolved"
        assert result["reason"] == "no longer needed"

        loaded = hs._load(sub["swarm_id"])
        assert loaded["status"] == "dissolved"

    def test_dissolve_nonexistent(self):
        result = hs.dissolve_sub_swarm("ghost")
        assert "error" in result

    def test_dissolve_propagates(self):
        s1 = hs.create_sub_swarm("root", "only-child", "t", "l")
        hs.dissolve_sub_swarm(s1["swarm_id"], "done")
        # Parent should see child dissolved; since only child + dissolved counts as complete
        parent = hs._load("root")
        assert parent["status"] in ("completed", "active")  # depends on propagation


# ── get_hierarchy_stats ──────────────────────────────────────────────────


class TestHierarchyStats:
    def test_single_root(self):
        hs._ensure_root("root")
        stats = hs.get_hierarchy_stats("root")
        assert stats["total"] == 1
        assert stats["total_sub_swarms"] == 0

    def test_with_children(self):
        hs.create_sub_swarm("root", "a", "t", "lead-1")
        hs.create_sub_swarm("root", "b", "t", "lead-2")
        stats = hs.get_hierarchy_stats("root")
        assert stats["total"] == 3
        assert stats["total_sub_swarms"] == 2
        assert stats["total_agents"] >= 2  # at least the lead agents

    def test_completion_percentage(self):
        s1 = hs.create_sub_swarm("root", "a", "t", "l")
        s2 = hs.create_sub_swarm("root", "b", "t", "l")
        c1 = hs._load(s1["swarm_id"])
        c1["status"] = "completed"
        hs._save(c1)

        stats = hs.get_hierarchy_stats("root")
        # 1 of 3 completed
        assert stats["completed"] == 1
        assert stats["completion_pct"] == pytest.approx(33.3, abs=0.1)

    def test_nonexistent(self):
        result = hs.get_hierarchy_stats("ghost")
        assert "error" in result
