"""Tests for ApprovalManager."""

import pytest

from cyberbrain26.security_plane.approval import (
    ApprovalDecision,
    ApprovalManager,
    ApprovalRecord,
    ApprovalRequest,
    ApprovalStatus,
)
from cyberbrain26.security_plane.identity import (
    AgentIdentity,
    PermissionScope,
    TrustLevel,
)


@pytest.fixture
def agent() -> AgentIdentity:
    return AgentIdentity(
        agent_id="agent-1",
        role="coder",
        trust_level=TrustLevel.TRUSTED,
        scopes={PermissionScope.SHELL},
    )


@pytest.fixture
def manager() -> ApprovalManager:
    return ApprovalManager()


class TestCreateRequest:
    def test_create_request_returns_request(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell", "/bin/rm")
        assert isinstance(request, ApprovalRequest)
        assert request.action == "execute_shell"
        assert request.resource == "/bin/rm"
        assert request.status == ApprovalStatus.PENDING
        assert request.agent == agent

    def test_create_request_has_unique_id(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        r1 = manager.create_request(agent, "execute_shell")
        r2 = manager.create_request(agent, "execute_shell")
        assert r1.request_id != r2.request_id

    def test_create_request_with_context(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(
            agent,
            "execute_shell",
            "/bin/rm",
            context={"env": "production"},
            risk_score=0.9,
        )
        assert request.context == {"env": "production"}
        assert request.risk_score == 0.9


class TestApprove:
    def test_approve_pending_request(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        result = manager.approve(request.request_id, "user@example.com")
        assert result is not None
        assert result.status == ApprovalStatus.APPROVED
        assert result.decision == ApprovalDecision.APPROVED
        assert result.reviewer == "user@example.com"

    def test_approve_nonexistent_returns_none(
        self, manager: ApprovalManager
    ) -> None:
        result = manager.approve("nonexistent", "user@example.com")
        assert result is None

    def test_approve_already_decided_returns_none(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        manager.approve(request.request_id, "user@example.com")
        result = manager.approve(request.request_id, "user2@example.com")
        assert result is None

    def test_approve_creates_record(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell", "/bin/rm")
        manager.approve(request.request_id, "user@example.com", "Approved")
        record = manager.get_record(request.request_id)
        assert record is not None
        assert isinstance(record, ApprovalRecord)
        assert record.decision == ApprovalDecision.APPROVED
        assert record.reviewer == "user@example.com"
        assert record.reason == "Approved"


class TestDeny:
    def test_deny_pending_request(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        result = manager.deny(request.request_id, "user@example.com", "Too risky")
        assert result is not None
        assert result.status == ApprovalStatus.DENIED
        assert result.decision == ApprovalDecision.DENIED

    def test_deny_creates_record(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        manager.deny(request.request_id, "user@example.com", "Too risky")
        record = manager.get_record(request.request_id)
        assert record is not None
        assert record.decision == ApprovalDecision.DENIED
        assert record.reason == "Too risky"


class TestQueryHelpers:
    def test_get_request(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        found = manager.get_request(request.request_id)
        assert found is request

    def test_get_request_nonexistent(
        self, manager: ApprovalManager
    ) -> None:
        assert manager.get_request("nonexistent") is None

    def test_get_record_nonexistent(
        self, manager: ApprovalManager
    ) -> None:
        assert manager.get_record("nonexistent") is None

    def test_list_pending_empty(
        self, manager: ApprovalManager
    ) -> None:
        assert manager.list_pending() == []

    def test_list_pending_returns_pending(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        r1 = manager.create_request(agent, "execute_shell")
        r2 = manager.create_request(agent, "execute_shell")
        manager.approve(r1.request_id, "user@example.com")
        pending = manager.list_pending()
        assert len(pending) == 1
        assert pending[0].request_id == r2.request_id

    def test_list_records_empty(
        self, manager: ApprovalManager
    ) -> None:
        assert manager.list_records() == []

    def test_list_records_all(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        r1 = manager.create_request(agent, "execute_shell")
        r2 = manager.create_request(agent, "execute_shell")
        manager.approve(r1.request_id, "user@example.com")
        manager.deny(r2.request_id, "user2@example.com")
        records = manager.list_records()
        assert len(records) == 2

    def test_list_records_filtered_by_agent(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        r1 = manager.create_request(agent, "execute_shell")
        manager.approve(r1.request_id, "user@example.com")
        records = manager.list_records(agent_id="agent-1")
        assert len(records) == 1
        records_other = manager.list_records(agent_id="other")
        assert len(records_other) == 0


class TestCancel:
    def test_cancel_pending(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        result = manager.cancel(request.request_id)
        assert result is not None
        assert result.status == ApprovalStatus.CANCELLED

    def test_cancel_nonexistent(
        self, manager: ApprovalManager
    ) -> None:
        assert manager.cancel("nonexistent") is None

    def test_cancel_already_decided(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        manager.approve(request.request_id, "user@example.com")
        result = manager.cancel(request.request_id)
        assert result is None


class TestExpired:
    def test_check_expired_no_pending(
        self, manager: ApprovalManager, agent: AgentIdentity
    ) -> None:
        request = manager.create_request(agent, "execute_shell")
        manager.approve(request.request_id, "user@example.com")
        expired = manager.check_expired()
        assert expired == []

    def test_default_timeout(self) -> None:
        manager = ApprovalManager()
        assert manager.timeout_seconds == 3600.0

    def test_custom_timeout(self) -> None:
        manager = ApprovalManager(timeout_seconds=60.0)
        assert manager.timeout_seconds == 60.0