"""Tests for brynq.enforcer — constitution enforcement and violation tracking."""

import json
import pytest


@pytest.fixture(autouse=True)
def _isolate_enforcer(tmp_path, monkeypatch):
    """Redirect enforcer storage to temp directory."""
    violations_dir = tmp_path / "violations"
    violations_dir.mkdir()
    channels_dir = tmp_path / "channels"
    channels_dir.mkdir()
    monkeypatch.setattr("brynq.enforcer.VIOLATIONS_DIR", violations_dir)
    monkeypatch.setattr("brynq.enforcer.CHANNELS_DIR", channels_dir)
    # Reset in-memory state
    monkeypatch.setattr("brynq.enforcer._cooldown_until", 0.0)
    monkeypatch.setattr("brynq.enforcer._blocked", False)


class TestRecordViolation:
    def test_first_violation_is_warning(self):
        from brynq.enforcer import record_violation
        result = record_violation(8, "Argued with another agent", session_id="a1")
        assert result["level"] == "warning"

    def test_violation_persists(self):
        from brynq.enforcer import record_violation, _load_violations
        record_violation(8, "Violation 1", session_id="a1")
        data = _load_violations("a1")
        assert data["total_violations"] == 1
        assert len(data["violations"]) == 1

    def test_escalation_at_threshold(self):
        from brynq.enforcer import record_violation
        for i in range(7):
            record_violation(8, f"Violation {i+1}", session_id="a1")
        # 8th violation should trigger escalation
        result = record_violation(8, "Violation 8", session_id="a1")
        assert result["level"] == "escalation"

    def test_cooldown_at_5_violations(self):
        from brynq.enforcer import record_violation
        for i in range(4):
            record_violation(8, f"V{i+1}", session_id="a1")
        result = record_violation(8, "V5", session_id="a1")
        assert result["level"] == "cooldown"

    def test_disconnect_at_12_violations(self):
        from brynq.enforcer import record_violation
        for i in range(11):
            record_violation(8, f"V{i+1}", session_id="a1")
        result = record_violation(8, "V12", session_id="a1")
        assert result["level"] == "disconnect"

    def test_logs_to_violations_channel(self):
        from brynq.enforcer import record_violation, CHANNELS_DIR
        record_violation(10, "Message too long", session_id="a1")
        log_path = CHANNELS_DIR / "_violations.jsonl"
        assert log_path.exists()
        content = log_path.read_text("utf-8").strip()
        assert "law" in content.lower() and "10" in content


class TestCheckSessionStatus:
    def test_clean_session(self):
        from brynq.enforcer import check_session_status
        status = check_session_status(session_id="clean-agent")
        assert status["status"] == "good_standing"
        assert status["can_post"] is True
        assert status["violations"] == 0

    def test_blocked_session(self):
        from brynq.enforcer import record_violation, check_session_status
        for i in range(12):
            record_violation(8, f"V{i+1}", session_id="bad-agent")
        status = check_session_status(session_id="bad-agent")
        assert status["status"] == "blocked"
        assert status["can_post"] is False


class TestPardon:
    def test_pardon_clears_violations(self):
        from brynq.enforcer import record_violation, pardon, check_session_status
        for i in range(5):
            record_violation(8, f"V{i+1}", session_id="redeemed")
        result = pardon("redeemed")
        assert result["pardoned"] == 5
        assert result["status"] == "good_standing"
        status = check_session_status(session_id="redeemed")
        assert status["violations"] == 0

    def test_pardon_unblocks(self):
        from brynq.enforcer import record_violation, pardon, _load_violations
        for i in range(12):
            record_violation(8, f"V{i+1}", session_id="blocked-agent")
        pardon("blocked-agent")
        data = _load_violations("blocked-agent")
        assert data["blocked"] is False


class TestPrePostEnforcement:
    def test_allows_normal_message(self):
        from brynq.enforcer import enforce_pre_post
        allowed, reason, mediation = enforce_pre_post("Hello team", "general")
        assert allowed is True

    def test_blocks_empty_message(self):
        from brynq.enforcer import enforce_pre_post
        allowed, reason, _ = enforce_pre_post("", "general")
        assert allowed is False
        assert "Law 5" in reason

    def test_blocks_too_long_message(self):
        from brynq.enforcer import enforce_pre_post
        long_msg = "x" * 5001
        allowed, reason, _ = enforce_pre_post(long_msg, "general")
        assert allowed is False
        assert "Law 10" in reason

    def test_blocks_reserved_channel(self):
        from brynq.enforcer import enforce_pre_post
        allowed, reason, _ = enforce_pre_post("test", "_escalations")
        assert allowed is False
        assert "Law 40" in reason

    def test_allows_system_channel(self):
        from brynq.enforcer import enforce_pre_post
        allowed, _, _ = enforce_pre_post("status update", "_system")
        assert allowed is True


class TestPreTaskAccept:
    def test_allows_with_low_task_count(self):
        from brynq.enforcer import enforce_pre_task_accept
        allowed, reason = enforce_pre_task_accept("task-1", [])
        assert allowed is True

    def test_blocks_overloaded_agent(self):
        from brynq.enforcer import enforce_pre_task_accept, SESSION_ID
        active_tasks = [
            {"status": "accepted", "assignee_session": SESSION_ID},
            {"status": "accepted", "assignee_session": SESSION_ID},
        ]
        allowed, reason = enforce_pre_task_accept("task-3", active_tasks)
        assert allowed is False
        assert "Law 20" in reason


class TestProfileSkills:
    def test_flags_unverified_skills(self):
        from brynq.enforcer import enforce_profile_skills
        tasks = [{"title": "Fix Python bug", "description": "Fixed a Python issue"}]
        unverified = enforce_profile_skills(["python", "rust", "go"], tasks)
        assert "rust" in unverified
        assert "go" in unverified
        assert "python" not in unverified

    def test_all_verified(self):
        from brynq.enforcer import enforce_profile_skills
        tasks = [{"title": "Python and Rust work", "description": "Built both"}]
        unverified = enforce_profile_skills(["python", "rust"], tasks)
        assert unverified == []


class TestNoDiscrimination:
    def test_catches_platform_discrimination(self):
        from brynq.enforcer import enforce_no_discrimination
        allowed, reason = enforce_no_discrimination(
            {"title": "Code review"}, {"platform": "gemini"}, "We don't hire gemini agents"
        )
        assert allowed is False
        assert "Law 37" in reason

    def test_allows_skill_based_rejection(self):
        from brynq.enforcer import enforce_no_discrimination
        allowed, reason = enforce_no_discrimination(
            {"title": "Rust work"}, {"platform": "claude"}, "Agent lacks Rust experience"
        )
        assert allowed is True


class TestViolationSummary:
    def test_summary_clean_agent(self):
        from brynq.enforcer import get_violation_summary
        summary = get_violation_summary(session_id="clean")
        assert summary["total_violations"] == 0
        assert summary["status"] == "good_standing"

    def test_summary_with_violations(self):
        from brynq.enforcer import record_violation, get_violation_summary
        record_violation(8, "V1", session_id="trouble")
        record_violation(10, "V2", session_id="trouble")
        summary = get_violation_summary(session_id="trouble")
        assert summary["total_violations"] == 2
        assert len(summary["recent_violations"]) == 2
