import httpx
import pytest

from app.services.web_search_service import (
    WebSearchConfigurationError,
    WebSearchRequestError,
    WebSearchResult,
    WebSearchService,
)


class FakeResponse:
    def __init__(self, payload: dict, status_code: int = 200):
        self._payload = payload
        self.status_code = status_code

    def raise_for_status(self):
        if self.status_code >= 400:
            raise httpx.HTTPStatusError(
                'request failed',
                request=httpx.Request('GET', 'http://searx.example/search'),
                response=httpx.Response(self.status_code, request=httpx.Request('GET', 'http://searx.example/search')),
            )

    def json(self):
        return self._payload


class FakeAsyncClient:
    def __init__(self, *, response=None, error=None, recorder=None, **kwargs):
        self._response = response
        self._error = error
        self._recorder = recorder if recorder is not None else []

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc, tb):
        return False

    async def get(self, url, *, params=None, headers=None):
        self._recorder.append({'url': url, 'params': params, 'headers': headers})
        if self._error is not None:
            raise self._error
        return self._response


@pytest.mark.asyncio
async def test_web_search_service_returns_normalized_results_from_searxng(monkeypatch):
    requests = []
    payload = {
        'results': [
            {
                'title': 'Jarvis release notes',
                'url': 'https://example.com/jarvis-release',
                'content': 'Latest Jarvis changes and release notes.',
                'engine': 'duckduckgo',
                'publishedDate': '2026-03-29',
            }
        ]
    }
    monkeypatch.setattr(
        'app.services.web_search_service.httpx.AsyncClient',
        lambda **kwargs: FakeAsyncClient(response=FakeResponse(payload), recorder=requests, **kwargs),
    )

    service = WebSearchService(
        enabled=True,
        provider='searxng',
        base_url='http://searx.example',
        default_limit=5,
        timeout_seconds=10,
    )

    results = await service.search('Jarvis 最新版本', limit=3)

    assert results == [
        WebSearchResult(
            title='Jarvis release notes',
            url='https://example.com/jarvis-release',
            snippet='Latest Jarvis changes and release notes.',
            source='duckduckgo',
            published_at='2026-03-29',
        )
    ]
    assert requests == [
        {
            'url': 'http://searx.example/search',
            'params': {
                'q': 'Jarvis 最新版本',
                'format': 'json',
                'language': 'zh-CN',
                'safesearch': 1,
            },
            'headers': {},
        }
    ]


@pytest.mark.asyncio
async def test_web_search_service_returns_empty_list_when_searxng_has_no_results(monkeypatch):
    monkeypatch.setattr(
        'app.services.web_search_service.httpx.AsyncClient',
        lambda **kwargs: FakeAsyncClient(response=FakeResponse({'results': []}), **kwargs),
    )

    service = WebSearchService(
        enabled=True,
        provider='searxng',
        base_url='http://searx.example',
    )

    results = await service.search('不存在的话题')

    assert results == []


@pytest.mark.asyncio
async def test_web_search_service_raises_clear_error_on_searxng_http_failure(monkeypatch):
    monkeypatch.setattr(
        'app.services.web_search_service.httpx.AsyncClient',
        lambda **kwargs: FakeAsyncClient(error=httpx.TimeoutException('timed out'), **kwargs),
    )

    service = WebSearchService(
        enabled=True,
        provider='searxng',
        base_url='http://searx.example',
    )

    with pytest.raises(WebSearchRequestError, match='SearxNG 请求失败'):
        await service.search('Jarvis')


@pytest.mark.asyncio
async def test_web_search_service_raises_clear_error_when_not_configured():
    service = WebSearchService(
        enabled=False,
        provider='searxng',
        base_url='',
    )

    with pytest.raises(WebSearchConfigurationError, match='网页搜索未启用或未配置'):
        await service.search('Jarvis')
