"""Tests for AgentContext and approval helpers."""

import pytest

from agentbyte.context import AgentContext, ToolApprovalResponse
from agentbyte.messages import AssistantMessage, ToolCallRequest, UserMessage


def test_context_message_helpers() -> None:
    context = AgentContext()
    user = UserMessage(content="hello", source="user")
    assistant = AssistantMessage(content="hi", source="assistant")

    context.add_message(user)
    context.add_message(assistant)

    assert context.message_count == 2
    assert context.is_empty is False
    assert context.get_last_user_message() == user
    assert context.get_last_assistant_message() == assistant


def test_context_reset_clears_runtime_state() -> None:
    context = AgentContext(
        metadata={"request_id": "1"},
        shared_state={"value": 1},
        environment={"env": "dev"},
        session_id="session-1",
        user_id="user-42",
        title="My Session",
        tags=["prod", "demo"],
    )
    context.add_message(UserMessage(content="x", source="user"))

    context.reset()

    assert context.messages == []
    assert context.shared_state == {}
    assert context.metadata == {}
    assert context.environment == {"env": "dev"}
    assert context.session_id == "session-1"
    assert context.user_id == "user-42"
    assert context.title == "My Session"
    assert context.tags == ["prod", "demo"]
    assert context.updated_at is None


def test_new_fields_have_correct_defaults() -> None:
    context = AgentContext()
    assert context.user_id is None
    assert context.title is None
    assert context.tags == []
    assert context.updated_at is None


def test_new_fields_round_trip_serialisation() -> None:
    from datetime import datetime

    now = datetime(2026, 4, 2, 12, 0, 0)
    context = AgentContext(
        session_id="sess-1",
        user_id="user-99",
        title="Solar Research",
        tags=["demo", "prod"],
        updated_at=now,
    )
    data = context.model_dump(mode="json")
    restored = AgentContext.model_validate(data)

    assert restored.user_id == "user-99"
    assert restored.title == "Solar Research"
    assert restored.tags == ["demo", "prod"]
    assert restored.updated_at == now


def test_legacy_json_without_new_fields_deserialises() -> None:
    """Old serialised sessions missing the new fields must load cleanly."""
    legacy = {
        "messages": [],
        "metadata": {},
        "shared_state": {},
        "environment": {},
        "session_id": "old-session",
    }
    context = AgentContext.model_validate(legacy)
    assert context.session_id == "old-session"
    assert context.user_id is None
    assert context.title is None
    assert context.tags == []
    assert context.updated_at is None


def test_approval_lifecycle() -> None:
    context = AgentContext()
    tool_call = ToolCallRequest(
        tool_name="delete_file",
        parameters={"path": "/tmp/x"},
        call_id="call-1",
    )

    request = context.add_approval_request(tool_call=tool_call, tool_name="delete_file")
    assert context.waiting_for_approval is True

    response = request.create_response(approved=True)
    context.add_approval_response(response)

    approved = context.get_approved_tool_calls()
    assert len(approved) == 1
    assert approved[0].call_id == "call-1"
    assert context.waiting_for_approval is False


def test_rejected_approval_flow() -> None:
    context = AgentContext()
    tool_call = ToolCallRequest(
        tool_name="dangerous",
        parameters={"ok": False},
        call_id="call-2",
    )
    request = context.add_approval_request(tool_call=tool_call, tool_name="dangerous")
    context.add_approval_response(
        ToolApprovalResponse(
            request_id=request.request_id,
            tool_call_id=request.tool_call_id,
            approved=False,
            reason="denied",
        )
    )

    rejected = context.get_rejected_tool_calls()
    assert rejected[0][0] == "call-2"
    assert rejected[0][1].tool_name == "dangerous"


def test_approve_deny_helpers() -> None:
    context = AgentContext()
    first_call = ToolCallRequest(
        tool_name="sensitive",
        parameters={"x": 1},
        call_id="call-3",
    )
    second_call = ToolCallRequest(
        tool_name="sensitive",
        parameters={"x": 2},
        call_id="call-4",
    )

    context.add_approval_request(tool_call=first_call, tool_name="sensitive")
    context.add_approval_request(tool_call=second_call, tool_name="sensitive")

    approved_response = context.approve_tool("call-3", reason="ok")
    denied_response = context.deny_tool("call-4", reason="not allowed")

    assert approved_response.approved is True
    assert denied_response.approved is False


def test_approve_or_deny_unknown_tool_call_raises() -> None:
    context = AgentContext()

    with pytest.raises(ValueError, match="No pending tool call"):
        context.approve_tool("missing-call")

    with pytest.raises(ValueError, match="No pending tool call"):
        context.deny_tool("missing-call")
