"""Tests for browser-based login flow (agent-side)."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from cube_agent.auth.login import login_browser


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _mock_poll_response(status: str = "ready", api_key: str = "cmcp_test123"):
    """Create a mock httpx response for session polling."""
    resp = MagicMock()
    resp.status_code = 200
    if status == "ready":
        resp.json.return_value = {"status": "ready", "api_key": api_key}
    else:
        resp.json.return_value = {"status": "pending"}
    return resp


# ---------------------------------------------------------------------------
# Happy path
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_login_browser_success():
    """Browser login polls and saves API key on success."""
    # First call returns pending, second returns ready
    responses = [_mock_poll_response("pending"), _mock_poll_response("ready", "cmcp_browser123")]

    mock_client = AsyncMock()
    mock_client.get = AsyncMock(side_effect=responses)
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch("cube_agent.auth.login.httpx.AsyncClient", return_value=mock_client):
        with patch("cube_agent.auth.login.webbrowser.open") as mock_open:
            with patch("cube_agent.auth.login.save_key") as mock_save:
                with patch("cube_agent.auth.login.asyncio.sleep", new=AsyncMock()):
                    result = await login_browser()

    assert "Login successful" in result
    mock_open.assert_called_once()
    mock_save.assert_called_once_with("cmcp_browser123")


# ---------------------------------------------------------------------------
# Timeout
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_login_browser_timeout():
    """Browser login times out after max polls."""
    mock_client = AsyncMock()
    mock_client.get = AsyncMock(return_value=_mock_poll_response("pending"))
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch("cube_agent.auth.login.httpx.AsyncClient", return_value=mock_client):
        with patch("cube_agent.auth.login.webbrowser.open"):
            with patch("cube_agent.auth.login.save_key") as mock_save:
                with patch("cube_agent.auth.login.asyncio.sleep", new=AsyncMock()):
                    result = await login_browser()

    assert "timed out" in result.lower()
    mock_save.assert_not_called()


# ---------------------------------------------------------------------------
# Poll error resilience
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_login_browser_poll_error_then_success():
    """Network errors during polling are retried."""
    error_resp = Exception("Connection refused")
    success_resp = _mock_poll_response("ready", "cmcp_retry123")

    mock_client = AsyncMock()
    mock_client.get = AsyncMock(side_effect=[error_resp, error_resp, success_resp])
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch("cube_agent.auth.login.httpx.AsyncClient", return_value=mock_client):
        with patch("cube_agent.auth.login.webbrowser.open"):
            with patch("cube_agent.auth.login.save_key") as mock_save:
                with patch("cube_agent.auth.login.asyncio.sleep", new=AsyncMock()):
                    result = await login_browser()

    assert "Login successful" in result
    mock_save.assert_called_once_with("cmcp_retry123")


# ---------------------------------------------------------------------------
# Browser open
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_login_browser_opens_correct_url():
    """Browser login opens the correct cloud URL with session_id."""
    mock_client = AsyncMock()
    mock_client.get = AsyncMock(return_value=_mock_poll_response("ready"))
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch("cube_agent.auth.login.httpx.AsyncClient", return_value=mock_client):
        with patch("cube_agent.auth.login.webbrowser.open") as mock_open:
            with patch("cube_agent.auth.login.save_key"):
                with patch("cube_agent.auth.login.asyncio.sleep", new=AsyncMock()):
                    with patch("cube_agent.auth.login.get_cloud_url", return_value="https://cube.example.com"):
                        await login_browser()

    url = mock_open.call_args[0][0]
    assert url.startswith("https://cube.example.com/auth/login?session_id=")
