from __future__ import annotations

import pytest

from app.core.service_guide_extractor import ServiceGuideExtractionInput, ServiceGuideExtractor
from app.schemas.service_guide_profile import StandardServiceGuideProfile


@pytest.mark.asyncio
async def test_extractor_detects_standard_service_guide_and_parses_key_sections() -> None:
    extractor = ServiceGuideExtractor()
    sample = """
事项名称：办理普通护照
日常用语：护照办理、普通护照
实施编码：12345
一、基础信息
服务对象：自然人
承诺办结时限：7个工作日
二、申请材料
| 材料名称 | 原件 | 复印件 | 备注 |
| --- | --- | --- | --- |
| 居民身份证 | 1 | 0 | 原件核验 |
三、收费项目
收费项目：工本费 120元
四、办理窗口
| 窗口名称 | 办理地点 | 办公电话 |
| --- | --- | --- |
| 出入境窗口 | 市民中心 | 02012345678 |
""".strip()

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-1",
            content_hash="hash-1",
            title="办理普通护照办事指南",
            doc_type="办事指南",
            knowledge_category="办事指南",
            acl_ids=[],
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is True
    assert result.scene_type == "standard_service_guide"
    assert result.profile is not None
    assert isinstance(result.profile, StandardServiceGuideProfile)
    assert result.profile.matter_name == "办理普通护照"
    assert result.profile.colloquial_names == ["护照办理", "普通护照"]
    assert result.profile.promised_time_limit_days == 7
    assert len(result.profile.materials) == 1
    assert result.profile.materials[0].material_name == "居民身份证"
    assert result.profile.fees[0].amount_value == 120.0
    assert result.profile.service_windows[0].window_name == "出入境窗口"


@pytest.mark.asyncio
async def test_extractor_returns_other_for_non_guide_content() -> None:
    extractor = ServiceGuideExtractor()
    sample = "关于印发年度工作要点的通知\n为做好年度重点工作，现印发如下。"

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-2",
            content_hash="hash-2",
            title="关于印发年度工作要点的通知",
            doc_type="通知",
            knowledge_category="政策与法规",
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is False
    assert result.scene_type == "other"
    assert result.profile is None


@pytest.mark.asyncio
async def test_extractor_falls_back_to_raw_rows_when_table_is_not_structured() -> None:
    extractor = ServiceGuideExtractor()
    sample = """
事项名称：办理普通护照
实施编码：12345
申请材料
1. 居民身份证原件
2. 符合要求的照片回执
""".strip()

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-3",
            content_hash="hash-3",
            title="办理普通护照办事指南",
            doc_type="办事指南",
            knowledge_category="办事指南",
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is True
    assert result.profile is not None
    assert len(result.profile.materials) == 2
    assert result.profile.materials[0].raw_row_text == "1. 居民身份证原件"
    assert result.warnings


@pytest.mark.asyncio
async def test_extractor_distinguishes_consultation_and_complaint_channels() -> None:
    extractor = ServiceGuideExtractor()
    sample = """
事项名称：办理普通护照
咨询与监督投诉
咨询电话：12367
咨询网址：https://example.gov.cn/consult
投诉电话：12345
投诉网址：https://example.gov.cn/complaint
""".strip()

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-4",
            content_hash="hash-4",
            title="办理普通护照办事指南",
            doc_type="办事指南",
            knowledge_category="办事指南",
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is True
    assert result.profile is not None
    consultation = result.profile.consultation_and_supervision
    assert consultation.consultation_phones == ["12367"]
    assert consultation.complaint_phones == ["12345"]
    assert consultation.consultation_urls == ["https://example.gov.cn/consult"]
    assert consultation.complaint_urls == ["https://example.gov.cn/complaint"]


@pytest.mark.asyncio
async def test_extractor_preserves_multiline_field_value() -> None:
    extractor = ServiceGuideExtractor()
    sample = """
事项名称：办理普通护照
必须现场办理原因：申请人需到场核验身份
并采集生物特征信息
办理方式：窗口办理
""".strip()

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-5",
            content_hash="hash-5",
            title="办理普通护照办事指南",
            doc_type="办事指南",
            knowledge_category="办事指南",
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is True
    assert result.profile is not None
    assert result.profile.must_onsite_reason == "申请人需到场核验身份\n并采集生物特征信息"


def test_normalize_requirement_level_returns_empty_for_unknown_text() -> None:
    extractor = ServiceGuideExtractor()

    assert extractor._normalize_requirement_level("乱码字段") == ""
    assert extractor._normalize_requirement_level("必须提交") == "required"


@pytest.mark.asyncio
async def test_extractor_returns_detected_without_profile_when_profile_validation_fails(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    extractor = ServiceGuideExtractor()

    def _bad_materials(self, section_text: str):
        return ["invalid-material-row"], []

    def _stub_root_fields(
        self,
        matter_identity,
        basic_info,
        cross_region_service,
        materials,
        fees,
        service_windows,
        legal_basis,
        quality,
    ):
        return {"matter_name": matter_identity.get("matter_name", "")}

    monkeypatch.setattr(ServiceGuideExtractor, "_extract_materials", _bad_materials)
    monkeypatch.setattr(ServiceGuideExtractor, "_build_root_fields", _stub_root_fields)

    sample = """
事项名称：办理普通护照
实施编码：12345
申请材料
1. 居民身份证原件
""".strip()

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-bad-profile",
            content_hash="hash-bad-profile",
            title="办理普通护照办事指南",
            doc_type="办事指南",
            knowledge_category="办事指南",
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is True
    assert result.profile is None
    assert result.quality["needs_review"] is True
    assert any("profile validation failed" in warning for warning in result.warnings)
    assert "profile_validation_errors" in result.artifacts


@pytest.mark.asyncio
async def test_extractor_returns_detected_without_profile_when_section_extraction_raises(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    extractor = ServiceGuideExtractor()

    def _boom(self, section_text: str):
        raise RuntimeError("materials extraction boom")

    monkeypatch.setattr(ServiceGuideExtractor, "_extract_materials", _boom)

    sample = """
事项名称：办理普通护照
实施编码：12345
申请材料
1. 居民身份证原件
""".strip()

    result = await extractor.extract(
        ServiceGuideExtractionInput(
            doc_id="doc-stage-fail",
            content_hash="hash-stage-fail",
            title="办理普通护照办事指南",
            doc_type="办事指南",
            knowledge_category="办事指南",
            plain_text=sample,
            markdown_text=sample,
        )
    )

    assert result.detected is True
    assert result.profile is None
    assert result.quality["needs_review"] is True
    assert any("materials extraction boom" in warning for warning in result.warnings)
    assert result.artifacts["extract_error"] == "materials extraction boom"