import pytest
import aiohttp
from unittest.mock import patch, AsyncMock
from app.core.providers.stt.gemini import GoogleSTTProvider
from fastapi import HTTPException
from unittest.mock import AsyncMock, MagicMock

# Helper to create an async context manager mock with expected behavior
def create_async_context_manager_mock(status_code=200, headers=None, json_data=None):
    mock = AsyncMock()
    mock.status = status_code
    mock.headers = headers or {}
    mock.json = AsyncMock(return_value=json_data)
    # raise_for_status is synchronous in aiohttp, so use MagicMock, not AsyncMock
    mock.raise_for_status = MagicMock(return_value=None)
    mock.__aenter__.return_value = mock
    mock.__aexit__.return_value = None
    return mock

@pytest.mark.asyncio
@patch('aiohttp.ClientSession.post')
async def test_transcribe_audio_success(mock_post):
    provider = GoogleSTTProvider(api_key="fake-key")

    mock_start_resp = create_async_context_manager_mock(
        headers={"X-Goog-Upload-URL": "https://fake-upload-url"}
    )
    mock_upload_resp = create_async_context_manager_mock(
        json_data={"file": {"name": "files/123"}}
    )
    mock_transcribe_resp = create_async_context_manager_mock(
        json_data={
            "candidates": [
                {
                    "content": {
                        "parts": [{"text": "Hello world"}]
                    }
                }
            ]
        }
    )

    # Make the side_effect return the mocks directly (no coroutine)
    mock_post.side_effect = [
        mock_start_resp,
        mock_upload_resp,
        mock_transcribe_resp,
    ]

    result = await provider.transcribe_audio(b"fake-bytes")
    assert result == "Hello world"
    assert mock_post.call_count == 3

@pytest.mark.asyncio
@patch('aiohttp.ClientSession.post')
async def test_transcribe_audio_no_upload_url(mock_post):
    provider = GoogleSTTProvider(api_key="fake-key")

    mock_start_resp = create_async_context_manager_mock(headers={})
    mock_post.side_effect = [mock_start_resp]

    with pytest.raises(HTTPException) as exc:
        await provider.transcribe_audio(b"fake-bytes")

    assert exc.value.status_code == 500
    assert "No upload URL" in exc.value.detail
    assert mock_post.call_count == 1

@pytest.mark.asyncio
@patch('aiohttp.ClientSession.post')
async def test_transcribe_audio_malformed_response(mock_post):
    provider = GoogleSTTProvider(api_key="fake-key")

    mock_start_resp = create_async_context_manager_mock(
        headers={"X-Goog-Upload-URL": "https://fake-upload-url"}
    )
    mock_upload_resp = create_async_context_manager_mock(
        json_data={"file": {"name": "files/123"}}
    )
    mock_transcribe_resp = create_async_context_manager_mock(json_data={})

    mock_post.side_effect = [
        mock_start_resp,
        mock_upload_resp,
        mock_transcribe_resp,
    ]

    with pytest.raises(HTTPException) as exc:
        await provider.transcribe_audio(b"fake-bytes")

    assert exc.value.status_code == 500
    assert "Malformed API response" in exc.value.detail
    assert mock_post.call_count == 3
