"""Unit tests for LlmPipeline — mock LLM client and embedding provider."""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import json
import pytest


def _make_pipeline():
    with patch("app.infrastructure.perception.llm_pipeline.get_llm_client") as mock_llm_fn, \
         patch("app.infrastructure.perception.llm_pipeline.OpenAICompatibleEmbeddingProvider") as mock_emb_cls:

        mock_client = MagicMock()
        mock_client.chat.return_value = MagicMock(content='{"obligations":[{"text":"test obligation","deontic":"must","subject":"OEM","object":"system","condition":""}],"deadlines":[{"date":"2026-07-01","description":"实施截止"}],"scope":"适用于M1类车辆","penalties":"罚款","impact_level":"high"}')
        mock_llm_fn.return_value = mock_client

        mock_emb = MagicMock()
        mock_emb.embed_texts.return_value = [[0.1] * 1024, [0.9] * 1024]
        mock_emb_cls.return_value = mock_emb

        from app.infrastructure.perception.llm_pipeline import LlmPipeline
        return LlmPipeline(), mock_client, mock_emb


def test_extract_structure_returns_dict():
    pipeline, mock_client, _ = _make_pipeline()
    event = {
        "id": "evt-001",
        "standard_code": "GB 18384-2025",
        "title": "电动汽车安全要求",
        "summary": "新增 IP67 级别防护",
        "source_label": "CATARC",
        "tags": ["电池安全"],
    }
    result = pipeline.extract_structure(event)
    assert isinstance(result, dict)
    assert "obligations" in result
    assert "impact_level" in result


def test_assess_impact_returns_list():
    pipeline, mock_client, _ = _make_pipeline()
    mock_client.chat.return_value = MagicMock(content='[{"doc_id":"d1","doc_name":"Safety Manual","score":0.85,"key_clauses":"§4.2","recommendation":"更新第4章"}]')
    mock_retrieval = MagicMock()
    chunk = MagicMock()
    chunk.doc_id = "d1"
    chunk.doc_title = "Safety Manual"
    chunk.score = 0.85
    chunk.text = "relevant text"
    chunk.section_title = "§4.2"
    mock_retrieval.retrieve.return_value = [chunk]
    event = {
        "standard_code": "GB 18384-2025",
        "title": "电动汽车安全要求",
        "obligations": [{"text": "OEM shall comply"}],
    }
    result = pipeline.assess_impact(event, mock_retrieval)
    assert isinstance(result, list)


def test_compute_diff_no_change():
    pipeline, _, mock_emb = _make_pipeline()
    mock_emb.embed_texts.return_value = [[0.5] * 1024, [0.5] * 1024]
    result = pipeline.compute_diff("paragraph one", "paragraph one")
    assert isinstance(result, dict)
    assert "changed_sections" in result
    assert "change_summary" in result


def test_compute_diff_detects_change():
    pipeline, mock_client, mock_emb = _make_pipeline()
    mock_emb.embed_texts.return_value = [
        [1.0] + [0.0] * 1023,
        [0.0] + [1.0] + [0.0] * 1022,
    ]
    mock_client.chat.return_value = MagicMock(content='{"change_type":"tightened","summary":"Requirement tightened"}')
    result = pipeline.compute_diff("old paragraph text", "new tighter requirement text")
    assert isinstance(result["changed_sections"], list)
