"""Tests for dashboard tool confirmation flow using native Agno RunOutput."""

from unittest.mock import AsyncMock, MagicMock, patch


def _mock_completed_run(content="Just a reply."):
    """Create a mock RunOutput for a completed (no tool) run."""
    run = MagicMock()
    run.content = content
    run.status = "COMPLETED"
    run.active_requirements = []
    return run


def _mock_paused_run(tool_name="shell", tool_args=None, content="Sure, I can do that."):
    """Create a mock RunOutput for a paused run needing tool confirmation."""
    tool_args = tool_args or {"command": "echo hello"}

    te = MagicMock()
    te.tool_name = tool_name
    te.tool_args = tool_args

    req = MagicMock()
    req.needs_confirmation = True
    req.tool_execution = te

    run = MagicMock()
    run.content = content
    run.status = "PAUSED"
    run.active_requirements = [req]
    return run, req


def _mock_approval_item(item_id="test-approval-123"):
    """Create a mock ApprovalItem."""
    item = MagicMock()
    item.id = item_id
    return item


# ── Chat returns tool actions ────────────────────────────────────────────────


def test_chat_with_tool_call_shows_approval_card(client):
    """When Agno pauses for tool confirmation, the response includes an approval card."""
    run, _req = _mock_paused_run()
    item = _mock_approval_item()

    with (
        patch("dashboard.routes.agents.chat_with_tools", new_callable=AsyncMock, return_value=run),
        patch("timmy.approvals.create_item", return_value=item),
    ):
        response = client.post("/agents/default/chat", data={"message": "run echo hello"})

    assert response.status_code == 200
    assert "APPROVE" in response.text
    assert "REJECT" in response.text
    assert "shell" in response.text


def test_chat_without_tool_call_has_no_approval_card(client):
    """Normal responses without tool calls should not show approval buttons."""
    run = _mock_completed_run()

    with patch("dashboard.routes.agents.chat_with_tools", return_value=run):
        response = client.post("/agents/default/chat", data={"message": "hello"})

    assert response.status_code == 200
    assert "APPROVE" not in response.text
    assert "REJECT" not in response.text


def test_chat_tool_card_contains_impact_badge(client):
    """Tool approval cards should show impact level (shell = high)."""
    run, _req = _mock_paused_run()
    item = _mock_approval_item()

    with (
        patch("dashboard.routes.agents.chat_with_tools", new_callable=AsyncMock, return_value=run),
        patch("timmy.approvals.create_item", return_value=item),
    ):
        response = client.post("/agents/default/chat", data={"message": "run it"})

    assert "impact-high" in response.text


def test_chat_tool_card_has_htmx_approve_endpoint(client):
    """Approval card buttons should target the correct HTMX endpoints."""
    run, _req = _mock_paused_run()
    item = _mock_approval_item()

    with (
        patch("dashboard.routes.agents.chat_with_tools", new_callable=AsyncMock, return_value=run),
        patch("timmy.approvals.create_item", return_value=item),
    ):
        response = client.post("/agents/default/chat", data={"message": "run it"})

    assert 'hx-post="/agents/default/tool/' in response.text
    assert '/approve"' in response.text
    assert '/reject"' in response.text


# ── Approve endpoint ─────────────────────────────────────────────────────────


def _create_pending_tool(client, approval_id="test-approval-123"):
    """Helper: send a chat that creates a pending tool, return the approval_id."""
    run, _req = _mock_paused_run()
    item = _mock_approval_item(approval_id)

    with (
        patch("dashboard.routes.agents.chat_with_tools", new_callable=AsyncMock, return_value=run),
        patch("timmy.approvals.create_item", return_value=item),
    ):
        response = client.post("/agents/default/chat", data={"message": "run it"})

    assert 'hx-post="/agents/default/tool/' in response.text
    return approval_id


def test_approve_executes_tool_and_returns_result(client):
    """Approving a tool should resume via Agno and return the result card."""
    approval_id = _create_pending_tool(client)

    # Mock the resume returning a completed run with tool result
    result_run = MagicMock()
    result_tool = MagicMock()
    result_tool.tool_name = "shell"
    result_tool.result = "hello\n"
    result_run.tools = [result_tool]
    result_run.content = "Done."

    with (
        patch(
            "dashboard.routes.agents.continue_chat", new_callable=AsyncMock, return_value=result_run
        ),
        patch("timmy.approvals.approve"),
    ):
        response = client.post(f"/agents/default/tool/{approval_id}/approve")

    assert response.status_code == 200
    assert "hello" in response.text


def test_approve_unknown_id_returns_404(client):
    """Approving a non-existent tool action should return 404."""
    response = client.post("/agents/default/tool/nonexistent-id/approve")
    assert response.status_code == 404


def test_approve_same_id_twice_returns_404(client):
    """Each approval can only be used once."""
    approval_id = _create_pending_tool(client)

    result_run = _mock_completed_run("ok")

    with (
        patch(
            "dashboard.routes.agents.continue_chat", new_callable=AsyncMock, return_value=result_run
        ),
        patch("timmy.approvals.approve"),
    ):
        client.post(f"/agents/default/tool/{approval_id}/approve")

    # Second attempt should 404
    response = client.post(f"/agents/default/tool/{approval_id}/approve")
    assert response.status_code == 404


# ── Reject endpoint ──────────────────────────────────────────────────────────


def test_reject_returns_rejected_card(client):
    """Rejecting a tool should return a rejected status card."""
    approval_id = _create_pending_tool(client)

    with (
        patch(
            "dashboard.routes.agents.continue_chat",
            new_callable=AsyncMock,
            return_value=_mock_completed_run(),
        ),
        patch("timmy.approvals.reject"),
    ):
        response = client.post(f"/agents/default/tool/{approval_id}/reject")

    assert response.status_code == 200
    assert "Rejected" in response.text


def test_reject_unknown_id_still_returns_200(client):
    """Rejecting a non-existent ID is idempotent (no crash)."""
    with patch("timmy.approvals.reject"):
        response = client.post("/agents/default/tool/nonexistent-id/reject")
    assert response.status_code == 200


# ── Safe tools skip confirmation ─────────────────────────────────────────────


def test_safe_tool_does_not_show_approval(client):
    """Completed runs (safe tools auto-executed) should not show approval cards."""
    run = _mock_completed_run("Here is the file content.")

    with patch("dashboard.routes.agents.chat_with_tools", return_value=run):
        response = client.post("/agents/default/chat", data={"message": "read the readme"})

    assert "APPROVE" not in response.text
    assert "REJECT" not in response.text
