"""Tests for the action approval system."""

import pytest

from agent_scout.approval import (
    ApprovalManager,
    ApprovalStatus,
)
from agent_scout.events import EventBus


@pytest.fixture
def bus():
    return EventBus()


@pytest.fixture
def manager(bus):
    return ApprovalManager(event_bus=bus)


@pytest.mark.asyncio
async def test_request_approval(manager):
    request = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Buy MacBook Air M3",
        estimated_cost=999.0,
    )
    assert request.status == ApprovalStatus.PENDING
    assert request.task_id == "task-1"
    assert request.action_type == "purchase"
    assert request.estimated_cost == 999.0


@pytest.mark.asyncio
async def test_approve(manager):
    request = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Buy item",
    )
    result = await manager.approve(request.id, message="Go ahead!")
    assert result is not None
    assert result.status == ApprovalStatus.APPROVED
    assert result.response_message == "Go ahead!"
    assert result.resolved_at > 0
    # Should be removed from pending
    assert len(manager.get_pending()) == 0


@pytest.mark.asyncio
async def test_reject(manager):
    request = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Buy item",
    )
    result = await manager.reject(request.id, message="Too expensive")
    assert result is not None
    assert result.status == ApprovalStatus.REJECTED
    assert result.response_message == "Too expensive"


@pytest.mark.asyncio
async def test_modify(manager):
    request = await manager.request_approval(
        task_id="task-1",
        action_type="book",
        description="Book flight",
        details={"airline": "Delta", "price": 500},
    )
    result = await manager.modify(
        request.id,
        modifications={"price": 400, "class": "economy"},
        message="Get economy class instead",
    )
    assert result is not None
    assert result.status == ApprovalStatus.MODIFIED
    assert result.modifications["price"] == 400


@pytest.mark.asyncio
async def test_approve_nonexistent(manager):
    result = await manager.approve("nonexistent")
    assert result is None


@pytest.mark.asyncio
async def test_reject_nonexistent(manager):
    result = await manager.reject("nonexistent")
    assert result is None


@pytest.mark.asyncio
async def test_double_approve(manager):
    request = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Buy item",
    )
    await manager.approve(request.id)
    # Second approve should fail
    result = await manager.approve(request.id)
    assert result is None


@pytest.mark.asyncio
async def test_get_pending(manager):
    await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Item 1",
    )
    await manager.request_approval(
        task_id="task-2",
        action_type="book",
        description="Item 2",
    )
    pending = manager.get_pending()
    assert len(pending) == 2


@pytest.mark.asyncio
async def test_check_expired(manager):
    await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Slow decision",
        timeout_seconds=0.0,  # immediate expiry
    )
    expired = manager.check_expired()
    assert len(expired) == 1
    assert expired[0].status == ApprovalStatus.EXPIRED
    assert len(manager.get_pending()) == 0


@pytest.mark.asyncio
async def test_audit_log(manager):
    request = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Buy item",
    )
    await manager.approve(request.id)
    log = manager.get_audit_log()
    assert len(log) == 1
    assert log[0].status == ApprovalStatus.APPROVED
    assert log[0].task_id == "task-1"


@pytest.mark.asyncio
async def test_audit_log_filtered(manager):
    r1 = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Item 1",
    )
    r2 = await manager.request_approval(
        task_id="task-2",
        action_type="book",
        description="Item 2",
    )
    await manager.approve(r1.id)
    await manager.reject(r2.id)
    log = manager.get_audit_log(task_id="task-1")
    assert len(log) == 1
    assert log[0].task_id == "task-1"


@pytest.mark.asyncio
async def test_event_emission(bus, manager):
    events = []

    async def handler(event):
        events.append(event)

    bus.subscribe(handler)

    request = await manager.request_approval(
        task_id="task-1",
        action_type="purchase",
        description="Buy item",
    )
    assert len(events) == 1  # approval requested

    await manager.approve(request.id)
    assert len(events) == 2  # approval received
