"""Tests for the objection (itiraz / şekli inceleme yazısı) parser.

PDF metin çıkarımı ve kural eşleştirme deterministik olarak test edilir.
LLM ayıklaması API key gerektirir — bu testler API key olmadan
extracted=False yolunu doğrular. Gerçek PDF üretimi için soffice
gerekir; yoksa ilgili testler atlanır.
"""

from __future__ import annotations

import base64

import pytest
from httpx import ASGITransport, AsyncClient

from app.main import app
from app.services.docx import AmendmentInput, AmendmentItem, build_amendment_docx
from app.services.docx.pdf_converter import convert_docx_to_pdf, soffice_available
from app.services.formality.models import Jurisdiction
from app.services.objection.extractor import (
    ObjectionExtractionError,
    _extract_app_number,
    _match_rule,
    extract_objection_text,
    parse_objection,
)

# --- TR kural eşleştirme birim testleri ------------------------------------


def test_match_rule_uses_valid_llm_guess() -> None:
    """LLM geçerli bir kural kimliği önerirse o kullanılır."""
    assert (
        _match_rule(
            "herhangi bir metin", "claims.no_subheadings", Jurisdiction.TR
        )
        == "claims.no_subheadings"
    )


def test_match_rule_ignores_invalid_llm_guess() -> None:
    """Geçersiz LLM önerisi yok sayılır, anahtar kelimeye düşülür."""
    result = _match_rule(
        "İstemlerde alt başlık kullanılmamalı.",
        "uydurma.kural",
        Jurisdiction.TR,
    )
    assert result == "claims.no_subheadings"


def test_match_rule_keyword_fallback_abstract() -> None:
    result = _match_rule(
        "Özet başlığı altına buluş başlığı yazılmalı.", "", Jurisdiction.TR
    )
    assert result == "abstract.missing_title"


def test_match_rule_keyword_fallback_foreign_term() -> None:
    result = _match_rule(
        "Yabancı dildeki ifadelerin Türkçe karşılığı belirtilmeli.",
        "",
        Jurisdiction.TR,
    )
    assert result == "terms.foreign_phrase"


def test_match_rule_no_match_returns_empty() -> None:
    assert (
        _match_rule("Tamamen alakasız bir cümle.", "", Jurisdiction.TR) == ""
    )


# --- EP/US kural eşleştirme birim testleri ---------------------------------


def test_match_rule_epo_abstract_missing() -> None:
    assert (
        _match_rule(
            "Abstract is missing under EPC Art. 78.", "", Jurisdiction.EP
        )
        == "epo.abstract_missing"
    )


def test_match_rule_uspto_abstract_too_long() -> None:
    assert (
        _match_rule(
            "The abstract exceeds 150 words (37 CFR 1.72(b)).",
            "",
            Jurisdiction.US,
        )
        == "uspto.abstract_too_long"
    )


def test_match_rule_epo_does_not_use_us_rules() -> None:
    """EP araması USPTO kural kimliklerini kabul etmemeli."""
    # LLM bir US kuralı önerse bile, EP'de geçersiz olmalı.
    assert (
        _match_rule(
            "Some defect.", "uspto.abstract_missing", Jurisdiction.EP
        )
        == ""  # Geçerli değil; anahtar tablosunda da eşleşme yok.
    )


# --- Başvuru numarası regex (ofise göre) -----------------------------------


def test_extract_app_number_tr() -> None:
    assert (
        _extract_app_number(
            "Başvuru No: 2026/005880", Jurisdiction.TR
        )
        == "2026/005880"
    )


def test_extract_app_number_epo() -> None:
    assert (
        _extract_app_number(
            "Application no. 21123456.7", Jurisdiction.EP
        )
        == "21123456.7"
    )


def test_extract_app_number_uspto() -> None:
    assert (
        _extract_app_number(
            "Application No. 17/123,456", Jurisdiction.US
        )
        == "17/123,456"
    )


def test_extract_app_number_returns_empty_when_not_found() -> None:
    assert _extract_app_number("no number here", Jurisdiction.TR) == ""


# --- PDF metin çıkarımı -----------------------------------------------------


def test_extract_text_rejects_non_pdf() -> None:
    with pytest.raises(ObjectionExtractionError):
        extract_objection_text(b"bu bir PDF degil")


@pytest.mark.skipif(
    not soffice_available(), reason="LibreOffice (soffice) kurulu değil"
)
@pytest.mark.asyncio
async def test_extract_text_from_real_pdf() -> None:
    """Gerçek bir DOCX→PDF'ten metin çıkarımı çalışmalı."""
    docx = build_amendment_docx(
        AmendmentInput(
            application_number="2026/005880",
            invention_title="TEST BULUŞU",
            items=[
                AmendmentItem(
                    finding_title="İstemlerde alt başlık",
                    change_made="Alt başlıklar kaldırıldı.",
                )
            ],
        )
    )
    pdf_bytes = await convert_docx_to_pdf(docx)
    text = extract_objection_text(pdf_bytes)
    assert "2026/005880" in text
    assert "TEST BULUŞU" in text


# --- parse_objection (LLM yokken) ------------------------------------------


@pytest.mark.asyncio
async def test_parse_objection_without_api_key_extracts_app_number() -> None:
    """API key yoksa extracted=False ama başvuru no regex ile yakalanır."""
    text = (
        "T.C. TÜRK PATENT VE MARKA KURUMU\n"
        "Konu: Şekli İnceleme - 2026/005880 Numaralı Başvuru\n"
        "İstemler alt başlıklar şeklinde yazılmaz."
    )
    report = await parse_objection(text)
    # API key yoksa extracted False; varsa True — her iki halde de
    # başvuru numarası dolu olmalı.
    assert report.application_number == "2026/005880"


# --- Endpoint testleri ------------------------------------------------------


@pytest.mark.asyncio
async def test_objection_endpoint_rejects_bad_base64() -> None:
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        resp = await client.post(
            "/api/v1/objection/parse",
            json={"pdf_base64": "!!!gecersiz!!!base64!!!"},
        )
    assert resp.status_code == 400


@pytest.mark.asyncio
async def test_objection_endpoint_rejects_non_pdf() -> None:
    transport = ASGITransport(app=app)
    payload = base64.b64encode(b"bu bir PDF degil sadece metin").decode()
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        resp = await client.post(
            "/api/v1/objection/parse",
            json={"pdf_base64": payload},
        )
    assert resp.status_code == 400


@pytest.mark.skipif(
    not soffice_available(), reason="LibreOffice (soffice) kurulu değil"
)
@pytest.mark.asyncio
async def test_objection_endpoint_accepts_valid_pdf() -> None:
    """Geçerli PDF 200 dönmeli (LLM olmasa bile rapor iskeleti döner)."""
    docx = build_amendment_docx(
        AmendmentInput(
            application_number="2026/005880",
            invention_title="TEST",
            items=[
                AmendmentItem(
                    finding_title="x", change_made="y"
                )
            ],
        )
    )
    pdf_bytes = await convert_docx_to_pdf(docx)
    payload = base64.b64encode(pdf_bytes).decode()

    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        resp = await client.post(
            "/api/v1/objection/parse",
            json={"pdf_base64": payload},
        )
    assert resp.status_code == 200
    body = resp.json()
    assert "items" in body
    assert "extracted" in body


@pytest.mark.skipif(
    not soffice_available(), reason="LibreOffice (soffice) kurulu değil"
)
@pytest.mark.asyncio
async def test_objection_endpoint_accepts_jurisdiction() -> None:
    """Endpoint jurisdiction parametresini kabul etmeli; geçersiz değer 422."""
    import base64

    from httpx import ASGITransport, AsyncClient

    docx = build_amendment_docx(
        AmendmentInput(
            application_number="2026/005880",
            invention_title="TEST",
            items=[AmendmentItem(finding_title="x", change_made="y")],
        )
    )
    pdf_bytes = await convert_docx_to_pdf(docx)
    payload = base64.b64encode(pdf_bytes).decode()

    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        # Geçerli jurisdiction (EP) — 200
        ok = await client.post(
            "/api/v1/objection/parse",
            json={"pdf_base64": payload, "jurisdiction": "EP"},
        )
        assert ok.status_code == 200

        # Geçersiz jurisdiction (XX) — 422
        bad = await client.post(
            "/api/v1/objection/parse",
            json={"pdf_base64": payload, "jurisdiction": "XX"},
        )
        assert bad.status_code == 422
