"""Tests for Espacenet OPS client — httpx Mock Transport ile."""

from __future__ import annotations

import httpx
import pytest

from app.services.prior_art import (
    EspacenetAuthError,
    EspacenetClient,
    EspacenetError,
    EspacenetNotConfigured,
)

# Örnek EPO OPS biblio search response — gerçek structure'ı minimal korur.
_EPO_BIBLIO_JSON: dict = {
    "ops:world-patent-data": {
        "ops:biblio-search": {
            "ops:search-result": {
                "exchange-documents": [
                    {
                        "exchange-document": {
                            "@country": "EP",
                            "@doc-number": "1234567",
                            "@kind": "B1",
                            "bibliographic-data": {
                                "invention-title": [
                                    {"@lang": "en", "$": "A quantum-safe gateway"},
                                    {"@lang": "de", "$": "Ein quantensicheres Gateway"},
                                ],
                                "application-reference": {
                                    "document-id": [
                                        {
                                            "@document-id-type": "docdb",
                                            "date": {"$": "20210315"},
                                        },
                                    ]
                                },
                                "parties": {
                                    "applicants": {
                                        "applicant": [
                                            {
                                                "@data-format": "original",
                                                "applicant-name": {
                                                    "name": {"$": "ACME Crypto Inc."},
                                                },
                                            },
                                        ],
                                    },
                                },
                                "patent-classifications": {
                                    "patent-classification": [
                                        {
                                            "classification-scheme": {"$": "CPC"},
                                            "section": {"$": "H"},
                                            "class": {"$": "04"},
                                            "subclass": {"$": "L"},
                                            "main-group": {"$": "9"},
                                            "subgroup": {"$": "08"},
                                        },
                                    ],
                                },
                            },
                        },
                    },
                    {
                        "exchange-document": {
                            "@country": "US",
                            "@doc-number": "9876543",
                            "@kind": "B2",
                            "bibliographic-data": {
                                "invention-title": {"$": "Hybrid key exchange"},
                            },
                        },
                    },
                ],
            },
        },
    },
}


def _make_client(
    key: str | None = "test-key",
    secret: str | None = "test-secret",  # noqa: S107 — test fixture
    *,
    handler,
) -> EspacenetClient:
    """MockTransport ile gerçek network olmadan client inşa et."""
    transport = httpx.MockTransport(handler)
    http = httpx.AsyncClient(transport=transport, timeout=5.0)
    return EspacenetClient(key, secret, http_client=http)


@pytest.mark.asyncio
async def test_search_happy_path_parses_biblio_response() -> None:
    def handler(request: httpx.Request) -> httpx.Response:
        if "accesstoken" in request.url.path:
            return httpx.Response(200, json={"access_token": "tok_123", "expires_in": 1200})
        if "biblio" in request.url.path:
            return httpx.Response(200, json=_EPO_BIBLIO_JSON)
        return httpx.Response(500, text="unexpected")

    client = _make_client(handler=handler)
    try:
        hits = await client.search("txt=quantum", limit=5)
    finally:
        await client.close()

    assert len(hits) == 2
    ep, us = hits[0], hits[1]

    assert ep.source == "epo"
    assert ep.patent_no == "EP1234567B1"
    assert ep.title == "A quantum-safe gateway"
    assert ep.applicant == "ACME Crypto Inc."
    assert ep.filing_date == "2021-03-15"
    assert ep.cpc_classes == ["H04L9/08"]
    assert ep.url and "EP1234567B1" in ep.url

    assert us.patent_no == "US9876543B2"
    assert us.title == "Hybrid key exchange"
    assert us.applicant is None  # parties bloğu yok
    assert us.filing_date is None


@pytest.mark.asyncio
async def test_search_without_credentials_raises_not_configured() -> None:
    def handler(request: httpx.Request) -> httpx.Response:
        raise AssertionError("HTTP çağrısı yapılmamalıydı")

    client = _make_client(key=None, secret=None, handler=handler)
    try:
        with pytest.raises(EspacenetNotConfigured):
            await client.search("q=anything")
    finally:
        await client.close()


@pytest.mark.asyncio
async def test_auth_failure_raises_auth_error() -> None:
    def handler(request: httpx.Request) -> httpx.Response:
        if "accesstoken" in request.url.path:
            return httpx.Response(401, json={"error": "invalid_client"})
        return httpx.Response(500)

    client = _make_client(handler=handler)
    try:
        with pytest.raises(EspacenetAuthError):
            await client.search("q=anything")
    finally:
        await client.close()


@pytest.mark.asyncio
async def test_no_results_returns_empty_list() -> None:
    def handler(request: httpx.Request) -> httpx.Response:
        if "accesstoken" in request.url.path:
            return httpx.Response(200, json={"access_token": "t", "expires_in": 1200})
        return httpx.Response(404, text="no results")

    client = _make_client(handler=handler)
    try:
        hits = await client.search("q=nonexistent-gibberish-xyz")
    finally:
        await client.close()
    assert hits == []


@pytest.mark.asyncio
async def test_server_error_raises_espacenet_error() -> None:
    def handler(request: httpx.Request) -> httpx.Response:
        if "accesstoken" in request.url.path:
            return httpx.Response(200, json={"access_token": "t", "expires_in": 1200})
        return httpx.Response(500, text="EPO internal error")

    client = _make_client(handler=handler)
    try:
        with pytest.raises(EspacenetError) as exc_info:
            await client.search("q=anything")
    finally:
        await client.close()
    assert "500" in str(exc_info.value)


@pytest.mark.asyncio
async def test_token_is_cached_across_calls() -> None:
    """İki arda arda arama — token endpoint yalnızca bir kez çağrılmalı."""
    token_calls = 0

    def handler(request: httpx.Request) -> httpx.Response:
        nonlocal token_calls
        if "accesstoken" in request.url.path:
            token_calls += 1
            return httpx.Response(200, json={"access_token": "cached-tok", "expires_in": 1200})
        return httpx.Response(200, json={"ops:world-patent-data": {"ops:biblio-search": {}}})

    client = _make_client(handler=handler)
    try:
        await client.search("q=a")
        await client.search("q=b")
    finally:
        await client.close()

    assert token_calls == 1, f"Token yeniden alındı ({token_calls}x), cache bozuk"


def test_is_configured_reflects_credentials() -> None:
    c1 = EspacenetClient(None, None)
    c2 = EspacenetClient("k", "s")
    assert c1.is_configured() is False
    assert c2.is_configured() is True
