"""Tests for /api/v1/prior-art/search.

EspacenetClient + GooglePatentsClient + analyze_prior_art üçü de mock'lanır.
Gerçek network veya Claude çağrısı yok.
"""

from __future__ import annotations

from unittest.mock import AsyncMock, patch

import pytest
from httpx import ASGITransport, AsyncClient

from app.config import get_settings
from app.main import app
from app.services.prior_art import AnalysisItem, PriorArtHit


def _sample_payload() -> dict:
    return {
        "query": "post-quantum TLS gateway",
        "invention_summary": (
            "Bir post-kuantum TLS ağ geçidi olup, ML-DSA imzalı X25519 anahtar "
            "paylaşımını tek handshake içinde birleştirir."
        ),
        "include_epo": True,
        "include_google": True,
        "limit_per_source": 5,
    }


def _epo_hit(no: str, title: str) -> PriorArtHit:
    return PriorArtHit(source="epo", patent_no=no, title=title, applicant="EPO Co")


def _google_hit(no: str, title: str) -> PriorArtHit:
    return PriorArtHit(source="google", patent_no=no, title=title, applicant="Google Co")


@pytest.mark.asyncio
async def test_search_merges_and_ranks_by_similarity() -> None:
    """EPO + Google sonuçları birleştirilir, Claude ile analiz edilir, rank edilir."""
    get_settings.cache_clear()

    epo_results = [
        _epo_hit("EP1000001B1", "Classical TLS gateway"),
        _epo_hit("EP2000002B1", "Quantum-safe key exchange"),
    ]
    google_results = [
        _google_hit("US9876543B2", "Hybrid ML-DSA TLS"),
    ]
    analyses = [
        AnalysisItem(
            patent_no="EP1000001B1",
            similarity_score=20,
            risk_level="düşük",
            rationale="Klasik TLS, post-kuantum entegrasyonu yok.",
        ),
        AnalysisItem(
            patent_no="EP2000002B1",
            similarity_score=75,
            risk_level="yüksek",
            rationale="Kuantum-dirençli yaklaşım, bağımsız istem daraltılmalı.",
        ),
        AnalysisItem(
            patent_no="US9876543B2",
            similarity_score=55,
            risk_level="orta",
            rationale="Hibrit TLS var ama tek-handshake zinciri açık.",
        ),
    ]

    with patch.dict(
        "os.environ",
        {
            "ANTHROPIC_API_KEY": "sk-ant-test",
            "EPO_OPS_KEY": "key",
            "EPO_OPS_SECRET": "secret",
        },
        clear=False,
    ):
        get_settings.cache_clear()
        with (
            patch(
                "app.api.prior_art.EspacenetClient.search",
                new=AsyncMock(return_value=epo_results),
            ),
            patch(
                "app.api.prior_art.GooglePatentsClient.search",
                new=AsyncMock(return_value=google_results),
            ),
            patch(
                "app.api.prior_art.analyze_prior_art",
                new=AsyncMock(return_value=analyses),
            ),
        ):
            transport = ASGITransport(app=app)
            async with AsyncClient(transport=transport, base_url="http://test") as client:
                response = await client.post("/api/v1/prior-art/search", json=_sample_payload())

    assert response.status_code == 200
    body = response.json()
    assert body["analyzed"] is True
    assert body["total_hits"] == 3
    assert body["source_counts"]["epo"] == 2
    assert body["source_counts"]["google"] == 1

    # similarity_score DESC sıralama: 75 → 55 → 20
    hits = body["hits"]
    assert hits[0]["patent_no"] == "EP2000002B1"
    assert hits[0]["risk_level"] == "yüksek"
    assert hits[1]["patent_no"] == "US9876543B2"
    assert hits[2]["patent_no"] == "EP1000001B1"

    get_settings.cache_clear()


@pytest.mark.asyncio
async def test_dedup_prefers_epo_source_over_google() -> None:
    """Aynı patent hem EPO hem Google'da → EPO kaydı tutulur."""
    get_settings.cache_clear()

    epo_results = [_epo_hit("US9876543B2", "From EPO")]
    google_results = [_google_hit("US9876543B2", "From Google")]

    with patch.dict(
        "os.environ",
        {"EPO_OPS_KEY": "k", "EPO_OPS_SECRET": "s", "ANTHROPIC_API_KEY": ""},
        clear=False,
    ):
        get_settings.cache_clear()
        with (
            patch(
                "app.api.prior_art.EspacenetClient.search",
                new=AsyncMock(return_value=epo_results),
            ),
            patch(
                "app.api.prior_art.GooglePatentsClient.search",
                new=AsyncMock(return_value=google_results),
            ),
            patch(
                "app.api.prior_art.analyze_prior_art",
                new=AsyncMock(side_effect=AssertionError("Claude çağrılmamalıydı")),
            ),
        ):
            transport = ASGITransport(app=app)
            async with AsyncClient(transport=transport, base_url="http://test") as client:
                response = await client.post("/api/v1/prior-art/search", json=_sample_payload())

    body = response.json()
    assert body["total_hits"] == 1
    assert body["hits"][0]["source"] == "epo"
    assert body["hits"][0]["title"] == "From EPO"
    assert body["analyzed"] is False
    assert body["hits"][0]["similarity_score"] is None

    get_settings.cache_clear()


@pytest.mark.asyncio
async def test_search_without_epo_credentials_falls_back_to_google() -> None:
    """EPO credentials yok → sadece Google + epo_error flag'i."""
    get_settings.cache_clear()

    with patch.dict(
        "os.environ",
        {"EPO_OPS_KEY": "", "EPO_OPS_SECRET": "", "ANTHROPIC_API_KEY": ""},
        clear=False,
    ):
        get_settings.cache_clear()
        with (
            patch(
                "app.api.prior_art.GooglePatentsClient.search",
                new=AsyncMock(return_value=[_google_hit("US1B2", "google only")]),
            ),
            patch(
                "app.api.prior_art.analyze_prior_art",
                new=AsyncMock(side_effect=AssertionError("Claude çağrılmamalıydı")),
            ),
        ):
            transport = ASGITransport(app=app)
            async with AsyncClient(transport=transport, base_url="http://test") as client:
                response = await client.post("/api/v1/prior-art/search", json=_sample_payload())

    body = response.json()
    assert body["total_hits"] == 1
    assert body["source_counts"]["google"] == 1
    # EPO istendi ama configure değil → epo_error key
    assert "epo_error" in body["source_counts"]

    get_settings.cache_clear()


@pytest.mark.asyncio
async def test_search_validates_input() -> None:
    """Çok kısa query veya summary → 422."""
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        response = await client.post(
            "/api/v1/prior-art/search",
            json={"query": "ab", "invention_summary": "x"},  # hem query hem summary kısa
        )
    assert response.status_code == 422


@pytest.mark.asyncio
async def test_empty_results_dont_call_claude() -> None:
    """Hiç hit yoksa Claude çağrısı yapılmaz."""
    get_settings.cache_clear()

    with patch.dict(
        "os.environ",
        {"ANTHROPIC_API_KEY": "sk-ant-test", "EPO_OPS_KEY": "k", "EPO_OPS_SECRET": "s"},
        clear=False,
    ):
        get_settings.cache_clear()
        with (
            patch(
                "app.api.prior_art.EspacenetClient.search",
                new=AsyncMock(return_value=[]),
            ),
            patch(
                "app.api.prior_art.GooglePatentsClient.search",
                new=AsyncMock(return_value=[]),
            ),
            patch(
                "app.api.prior_art.analyze_prior_art",
                new=AsyncMock(side_effect=AssertionError("Claude çağrılmamalıydı")),
            ),
        ):
            transport = ASGITransport(app=app)
            async with AsyncClient(transport=transport, base_url="http://test") as client:
                response = await client.post("/api/v1/prior-art/search", json=_sample_payload())

    assert response.status_code == 200
    body = response.json()
    assert body["total_hits"] == 0
    # hits boş → analyzed false (analiz tetiklenmedi)
    assert body["analyzed"] is False

    get_settings.cache_clear()
