"""Tests for escalation dedup and rate limiting (Phase 7, Stream B)."""

import pytest
import json

from loom.bus.channels import escalation_guard_key
from loom.config import OrchestrationConfig


class TestEscalationGuardKey:
    def test_key_format(self):
        key = escalation_guard_key("proj-1", "task-1")
        assert key == "loom:proj-1:escalation:guard:task-1"


class TestOrchestrationConfigEscalation:
    def test_dedup_ttl_default(self):
        config = OrchestrationConfig()
        assert config.escalation_dedup_ttl_seconds == 600

    def test_max_escalations_default(self):
        config = OrchestrationConfig()
        assert config.max_escalations_per_tick == 5

    def test_custom_values(self):
        config = OrchestrationConfig(
            escalation_dedup_ttl_seconds=300,
            max_escalations_per_tick=10,
        )
        assert config.escalation_dedup_ttl_seconds == 300
        assert config.max_escalations_per_tick == 10


class TestEscalationDedup:
    async def test_dedup_guard_blocks_duplicate(self, redis_conn):
        """Second escalation for same task should be blocked by guard."""
        key = escalation_guard_key("proj-1", "task-1")
        # First set should succeed
        result1 = await redis_conn.set(key, "1", nx=True, ex=600)
        assert result1 is True
        # Second set should fail (guard exists)
        result2 = await redis_conn.set(key, "1", nx=True, ex=600)
        assert result2 is not True  # None or False

    async def test_dedup_guard_expires(self, redis_conn):
        """After TTL, guard should allow new escalation."""
        key = escalation_guard_key("proj-1", "task-2")
        # Set with 1-second TTL
        await redis_conn.set(key, "1", nx=True, ex=1)
        import asyncio
        await asyncio.sleep(1.1)
        # Should be expired now
        result = await redis_conn.set(key, "1", nx=True, ex=600)
        assert result is True


class TestBatchDeadLetter:
    async def test_batch_dead_letter_via_store(self, pool, project):
        """Test marking tasks as dead-lettered in batch."""
        from loom.graph.store import create_task, claim_task, fail_task, mark_dead_letter, get_task
        from loom.graph.task import Task, TaskStatus, Priority
        from loom.ids import task_id as gen_task_id

        # Create two tasks, claim and fail them
        tasks = []
        for i in range(2):
            t = Task(
                id=gen_task_id(), project_id=project,
                title=f"DL test {i}", status=TaskStatus.PENDING,
                priority=Priority.P1,
            )
            created = await create_task(pool, t)
            claimed = await claim_task(pool, created.id, "agent")
            failed = await fail_task(pool, created.id, "failed")
            tasks.append(failed)

        # Dead-letter them
        for t in tasks:
            await mark_dead_letter(pool, t.id, "bulk dead-letter")

        # Verify
        for t in tasks:
            updated = await get_task(pool, t.id)
            assert updated.dead_letter is True
