from __future__ import annotations

from typing import Any
from unittest.mock import AsyncMock, patch

import pytest
from fastapi import HTTPException, status
from roboco.api import deps as d
from roboco.api.deps import get_current_agent_slug


async def _run(
    dep: Any,
    headers: dict[str, str],
    settings_on: bool,
    monkeypatch: pytest.MonkeyPatch,
) -> Any:
    monkeypatch.setattr(d.settings, "cloud_auth_enabled", settings_on)
    db = AsyncMock()
    response = AsyncMock()
    # _cloud_auth_agent_context is the gate; stub it to assert it's reached
    with patch.object(d, "_cloud_auth_agent_context", new=AsyncMock()) as m:
        m.return_value = type(
            "Ctx",
            (),
            {"agent_id": "00000000-0000-0000-0000-000000000000", "slug": "be-dev-1"},
        )()
        return await dep(
            db=db,
            response=response,
            x_agent_id=headers.get("X-Agent-ID"),
            x_agent_role=headers.get("X-Agent-Role"),
            x_agent_team=headers.get("X-Agent-Team"),
            x_agent_token=headers.get("X-Agent-Token"),
            roboco_session=headers.get("roboco_session"),
        ), m.called


@pytest.mark.asyncio
async def test_cloud_auth_spoof_bare_agent_id_rejected(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    # A bare X-Agent-ID with no token/cookie must not reach the gate body.
    monkeypatch.setattr(d.settings, "cloud_auth_enabled", True)
    db = AsyncMock()
    response = AsyncMock()
    with patch.object(d, "_cloud_auth_agent_context", new=AsyncMock()) as m:
        m.side_effect = HTTPException(
            status.HTTP_401_UNAUTHORIZED,
            "Cloud auth is enabled — agent requests require a valid token.",
        )
        with pytest.raises(HTTPException) as exc:
            await d.get_current_agent_slug(
                db=db,
                response=response,
                x_agent_id="be-dev-1",
                x_agent_role=None,
                x_agent_team=None,
                x_agent_token=None,
                roboco_session=None,
            )
        assert exc.value.status_code == status.HTTP_401_UNAUTHORIZED


@pytest.mark.asyncio
async def test_cloud_auth_routes_through_dual_path(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    _, called = await _run(
        get_current_agent_slug, {"X-Agent-ID": "be-dev-1"}, True, monkeypatch
    )
    assert called  # cloud mode delegates to _cloud_auth_agent_context


@pytest.mark.asyncio
async def test_dev_mode_unchanged_slug_returns_header(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    monkeypatch.setattr(d.settings, "cloud_auth_enabled", False)
    db = AsyncMock()
    response = AsyncMock()
    with patch.object(d, "_cloud_auth_agent_context", new=AsyncMock()) as m:
        slug = await d.get_current_agent_slug(
            db=db,
            response=response,
            x_agent_id="be-dev-1",
            x_agent_role=None,
            x_agent_team=None,
            x_agent_token=None,
            roboco_session=None,
        )
    assert slug == "be-dev-1"
    assert not m.called  # dev path does not invoke the cloud gate
