"""
Tests for Consensus-Building Multi-Agent Pattern.

This module tests the consensus-building example which demonstrates voting-based
decision-making using SharedMemoryPool for multi-agent collaboration.

Test Coverage:
- Proposal creation and storage
- Reviewer voting mechanisms
- Vote counting and consensus calculation
- Facilitator decision-making
- Consensus rules (2/3 threshold)
- Mixed voting scenarios
- Workflow execution

Pattern:
ProposerAgent creates proposal → writes to SharedMemoryPool
→ ReviewerAgents read proposal → each votes (approve/reject/modify)
→ votes written to SharedMemoryPool → FacilitatorAgent reads votes
→ calculates consensus → returns final decision

Agents Tested:
- ProposerAgent: Creates solution proposals
- ReviewerAgent: Reviews and votes on proposals (3 instances)
- FacilitatorAgent: Counts votes, determines consensus

Author: Kaizen Framework Team
Created: 2025-10-02 (Phase 5, Task 5E.1, Example 2)
Reference: supervisor-worker example, Phase 4 shared-insights
"""

# Standardized example loading
from example_import_helper import import_example_module

# Load consensus-building example
_consensus_module = import_example_module("examples/2-multi-agent/consensus-building")
ProposerAgent = _consensus_module.ProposerAgent
ReviewerAgent = _consensus_module.ReviewerAgent
FacilitatorAgent = _consensus_module.FacilitatorAgent
consensus_building_workflow = _consensus_module.consensus_building_workflow
ConsensusConfig = _consensus_module.ConsensusConfig

from kaizen.memory.shared_memory import SharedMemoryPool


class TestProposalCreation:
    """Test proposal creation and storage."""

    def test_proposer_creates_proposal(self):
        """Test proposer creates proposal for problem."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        proposer = ProposerAgent(config, pool, agent_id="proposer_1")

        # Create proposal
        result = proposer.propose("How to improve code review process?")

        # Should return proposal with reasoning
        assert "proposal" in result
        assert "reasoning" in result
        assert isinstance(result["proposal"], str)
        assert len(result["proposal"]) > 0

    def test_proposal_written_to_shared_memory(self):
        """Test proposal is written to shared memory with correct tags."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        proposer = ProposerAgent(config, pool, agent_id="proposer_1")

        # Create proposal
        proposer.propose("How to reduce technical debt?")

        # Verify proposal written to shared memory
        proposals = pool.read_relevant(
            agent_id="reviewer_1", tags=["proposal", "pending"], exclude_own=False
        )

        assert len(proposals) > 0

        # Proposal should have correct tags and segment
        proposal_insight = proposals[0]
        assert "proposal" in proposal_insight["tags"]
        assert "pending" in proposal_insight["tags"]
        assert proposal_insight["segment"] == "proposals"
        assert proposal_insight["importance"] == 0.9

    def test_proposal_has_reasoning(self):
        """Test proposal includes reasoning behind solution."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        proposer = ProposerAgent(config, pool, agent_id="proposer_1")

        # Create proposal
        result = proposer.propose("Should we migrate to microservices?")

        # Should have both proposal and reasoning
        assert "proposal" in result
        assert "reasoning" in result
        assert len(result["reasoning"]) > 0
        # Reasoning should be non-empty
        assert result["reasoning"] != ""


class TestReviewerVoting:
    """Test reviewer voting mechanisms."""

    def test_reviewer_votes_approve(self):
        """Test reviewer can vote to approve proposal."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        reviewer = ReviewerAgent(config, pool, agent_id="reviewer_1")

        # Review proposal
        proposal = "Implement automated testing for all pull requests"
        result = reviewer.review(proposal)

        # Should return vote
        assert "vote" in result
        assert "feedback" in result
        assert "confidence" in result
        # Vote should be one of the valid options
        assert result["vote"] in ["approve", "reject", "modify"]

    def test_reviewer_votes_reject(self):
        """Test reviewer can vote to reject proposal."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        reviewer = ReviewerAgent(config, pool, agent_id="reviewer_2")

        # Review proposal (mock will return valid vote)
        proposal = "Remove all unit tests to speed up development"
        result = reviewer.review(proposal)

        # Should return vote with feedback
        assert "vote" in result
        assert result["vote"] in ["approve", "reject", "modify"]
        assert "feedback" in result
        assert len(result["feedback"]) > 0

    def test_reviewer_votes_modify(self):
        """Test reviewer can vote to modify proposal."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        reviewer = ReviewerAgent(config, pool, agent_id="reviewer_3")

        # Review proposal
        proposal = "Migrate entire codebase to new framework"
        result = reviewer.review(proposal)

        # Should return vote with confidence
        assert "vote" in result
        assert result["vote"] in ["approve", "reject", "modify"]
        assert "confidence" in result

    def test_multiple_reviewers_vote(self):
        """Test multiple reviewers can vote independently."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # Create multiple reviewers
        reviewers = [
            ReviewerAgent(config, pool, agent_id="reviewer_1"),
            ReviewerAgent(config, pool, agent_id="reviewer_2"),
            ReviewerAgent(config, pool, agent_id="reviewer_3"),
        ]

        proposal = "Adopt new coding standards"

        # Each reviewer votes
        votes = []
        for reviewer in reviewers:
            vote = reviewer.review(proposal)
            votes.append(vote)

        # Should have 3 votes
        assert len(votes) == 3

        # Each vote should be valid
        for vote in votes:
            assert "vote" in vote
            assert vote["vote"] in ["approve", "reject", "modify"]

    def test_votes_written_to_shared_memory(self):
        """Test votes are written to shared memory with correct tags."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        reviewer = ReviewerAgent(config, pool, agent_id="reviewer_1")

        # Review proposal
        proposal = "Implement CI/CD pipeline"
        reviewer.review(proposal)

        # Verify vote written to shared memory
        votes = pool.read_relevant(
            agent_id="facilitator_1", tags=["vote"], exclude_own=False
        )

        assert len(votes) > 0

        # Vote should have correct tags and segment
        vote_insight = votes[0]
        assert "vote" in vote_insight["tags"]
        assert vote_insight["segment"] == "votes"
        assert vote_insight["importance"] == 0.8


class TestFacilitatorConsensus:
    """Test facilitator consensus calculation."""

    def test_facilitator_reads_votes(self):
        """Test facilitator reads all votes from shared memory."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # Simulate multiple votes
        pool.write_insight(
            {
                "agent_id": "reviewer_1",
                "content": '{"vote": "approve", "feedback": "Good idea"}',
                "tags": ["vote", "reviewer_1", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_2",
                "content": '{"vote": "approve", "feedback": "Agree"}',
                "tags": ["vote", "reviewer_2", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")

        # Read votes
        votes = facilitator.get_votes("proposal_1")

        # Should find votes
        assert len(votes) >= 2

    def test_consensus_with_all_approve(self):
        """Test consensus reached when all reviewers approve."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # Simulate unanimous approval
        for i in range(3):
            pool.write_insight(
                {
                    "agent_id": f"reviewer_{i+1}",
                    "content": '{"vote": "approve", "feedback": "Looks good"}',
                    "tags": ["vote", f"reviewer_{i+1}", "proposal_1"],
                    "importance": 0.8,
                    "segment": "votes",
                    "metadata": {"proposal_id": "proposal_1"},
                }
            )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")

        # Facilitate decision
        result = facilitator.facilitate("proposal_1")

        # Should reach consensus
        assert "decision" in result
        assert "rationale" in result
        assert "consensus_level" in result
        # Decision should be ACCEPT (all approved)
        assert result["decision"] in ["ACCEPT", "REJECT", "REQUEST_REVISION"]

    def test_no_consensus_with_split_votes(self):
        """Test no consensus when votes are split."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # Simulate split votes
        pool.write_insight(
            {
                "agent_id": "reviewer_1",
                "content": '{"vote": "approve", "feedback": "Good"}',
                "tags": ["vote", "reviewer_1", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_2",
                "content": '{"vote": "reject", "feedback": "Bad"}',
                "tags": ["vote", "reviewer_2", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_3",
                "content": '{"vote": "modify", "feedback": "Needs work"}',
                "tags": ["vote", "reviewer_3", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")

        # Facilitate decision
        result = facilitator.facilitate("proposal_1")

        # Should not reach clear consensus
        assert "decision" in result
        # With split votes, should request revision
        assert result["decision"] in ["REQUEST_REVISION", "REJECT"]

    def test_rejection_with_all_reject(self):
        """Test rejection when all reviewers reject."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # Simulate unanimous rejection
        for i in range(3):
            pool.write_insight(
                {
                    "agent_id": f"reviewer_{i+1}",
                    "content": '{"vote": "reject", "feedback": "Not viable"}',
                    "tags": ["vote", f"reviewer_{i+1}", "proposal_1"],
                    "importance": 0.8,
                    "segment": "votes",
                    "metadata": {"proposal_id": "proposal_1"},
                }
            )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")

        # Facilitate decision
        result = facilitator.facilitate("proposal_1")

        # Should reject
        assert "decision" in result
        # With all rejections, should be REJECT
        assert result["decision"] in ["REJECT", "REQUEST_REVISION"]

    def test_modify_votes_counted_as_partial(self):
        """Test modify votes are counted as partial approval (0.5 weight)."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # Simulate votes with modify
        pool.write_insight(
            {
                "agent_id": "reviewer_1",
                "content": '{"vote": "approve", "feedback": "Good"}',
                "tags": ["vote", "reviewer_1", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_2",
                "content": '{"vote": "modify", "feedback": "Needs changes"}',
                "tags": ["vote", "reviewer_2", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_3",
                "content": '{"vote": "modify", "feedback": "Some improvements needed"}',
                "tags": ["vote", "reviewer_3", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")

        # Facilitate decision
        result = facilitator.facilitate("proposal_1")

        # Should have consensus calculation
        assert "consensus_level" in result
        # 1 approve + 2 modify (0.5 each) = 2.0 / 3 = 0.67 (exactly at threshold)


class TestFullWorkflow:
    """Test full consensus-building workflow."""

    def test_full_consensus_workflow(self):
        """Test full workflow from problem to decision."""
        result = consensus_building_workflow("How to improve team collaboration?")

        # Should have complete result
        assert "problem" in result
        assert "proposal" in result
        assert "votes" in result
        assert "decision" in result
        assert "stats" in result

        # Should have 3 votes
        assert len(result["votes"]) == 3

    def test_workflow_with_approval(self):
        """Test workflow resulting in approval."""
        result = consensus_building_workflow("Should we adopt automated testing?")

        # Should have decision
        assert "decision" in result
        assert "decision" in result["decision"]
        # Decision should be one of the valid types
        assert result["decision"]["decision"] in [
            "ACCEPT",
            "REJECT",
            "REQUEST_REVISION",
        ]

    def test_workflow_with_rejection(self):
        """Test workflow resulting in rejection."""
        result = consensus_building_workflow("Should we remove all documentation?")

        # Should have decision with rationale
        assert "decision" in result
        assert "rationale" in result["decision"]
        assert len(result["decision"]["rationale"]) > 0

    def test_workflow_with_revision_request(self):
        """Test workflow resulting in revision request."""
        result = consensus_building_workflow("Should we refactor the entire codebase?")

        # Should have decision
        assert "decision" in result
        assert "consensus_level" in result["decision"]

    def test_stats_reflect_all_operations(self):
        """Test shared memory stats reflect all operations."""
        result = consensus_building_workflow("What's the best architecture pattern?")

        stats = result["stats"]

        # Should have accurate counts
        assert "insight_count" in stats
        assert "agent_count" in stats
        assert stats["insight_count"] > 0
        # Should have proposer + 3 reviewers + facilitator = 5 agents
        assert stats["agent_count"] >= 5


class TestConsensusRules:
    """Test consensus rule implementation."""

    def test_two_thirds_threshold_met(self):
        """Test 2/3 threshold is correctly applied."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # 2 approve, 1 reject = 2/3 = 0.67 (meets threshold)
        pool.write_insight(
            {
                "agent_id": "reviewer_1",
                "content": '{"vote": "approve", "feedback": "Yes"}',
                "tags": ["vote", "reviewer_1", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_2",
                "content": '{"vote": "approve", "feedback": "Yes"}',
                "tags": ["vote", "reviewer_2", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_3",
                "content": '{"vote": "reject", "feedback": "No"}',
                "tags": ["vote", "reviewer_3", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")
        result = facilitator.facilitate("proposal_1")

        # Should accept (2/3 threshold met)
        assert "decision" in result
        assert result["decision"] in ["ACCEPT", "REQUEST_REVISION"]

    def test_threshold_not_met(self):
        """Test below 2/3 threshold results in revision request."""
        pool = SharedMemoryPool()
        config = ConsensusConfig(llm_provider="mock", model="gpt-3.5-turbo")

        # 1 approve, 2 reject = 1/3 = 0.33 (below threshold)
        pool.write_insight(
            {
                "agent_id": "reviewer_1",
                "content": '{"vote": "approve", "feedback": "Yes"}',
                "tags": ["vote", "reviewer_1", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_2",
                "content": '{"vote": "reject", "feedback": "No"}',
                "tags": ["vote", "reviewer_2", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )
        pool.write_insight(
            {
                "agent_id": "reviewer_3",
                "content": '{"vote": "reject", "feedback": "No"}',
                "tags": ["vote", "reviewer_3", "proposal_1"],
                "importance": 0.8,
                "segment": "votes",
                "metadata": {"proposal_id": "proposal_1"},
            }
        )

        facilitator = FacilitatorAgent(config, pool, agent_id="facilitator_1")
        result = facilitator.facilitate("proposal_1")

        # Should request revision or reject
        assert "decision" in result
        assert result["decision"] in ["REJECT", "REQUEST_REVISION"]
