"""Test Rerank service implementation."""

import os
from unittest import mock

import pytest

from databuilder_logic_sdk.core.rerank import (
    QianFanReranker,
    RerankConfig,
    RerankResponse,
    RerankResult,
    RerankService,
    create_rerank_service,
)

from .utils import gen_test_config, reset_global_config


def test_create_rerank_service() -> None:
    """Test creating a rerank service."""
    service = create_rerank_service("bce-reranker-base", api_key="test-api-key", endpoints=["http://test-endpoint"])
    assert isinstance(service, RerankService)
    assert service.config.model_name == "bce-reranker-base"
    assert service.config.api_key == "test-api-key"
    assert service.config.endpoints == ["http://test-endpoint"]


def test_default_endpoints_from_config() -> None:
    """Test that default endpoints are taken from global config."""
    reset_global_config()

    mock_config = gen_test_config(
        rerank={
            "endpoint": "http://config-endpoint",
            "modelServingName": "test-serving",
            "baseModelName": "bce-reranker-base",
        }
    )
    with mock.patch.dict(os.environ, {"ServiceConfig": str(mock_config).replace("'", '"')}):
        service = create_rerank_service("bce-reranker-base", api_key="test-api-key")
        assert isinstance(service, RerankService)
        assert service.config.model_name == "bce-reranker-base"
        assert service.config.api_key == "test-api-key"


def test_api_key_from_config_when_arg_missing() -> None:
    """Test fallback to ServiceConfig.rerank.apiKey when api_key argument is missing."""
    reset_global_config()
    mock_config = gen_test_config(
        rerank={
            "endpoint": "http://config-endpoint",
            "modelServingName": "test-serving",
            "baseModelName": "bce-reranker-base",
            "apiKey": "config-api-key",
        }
    )

    with mock.patch.dict(os.environ, {"ServiceConfig": str(mock_config).replace("'", '"')}):
        service = create_rerank_service("bce-reranker-base")

    mock_response = mock.Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "results": [
            {
                "document": "doc1",
                "relevance_score": 0.9,
                "index": 0,
            }
        ]
    }
    with mock.patch("databuilder_logic_sdk.core.rerank.requests.post", return_value=mock_response) as mock_post:
        response = service.rerank("query", ["doc1"], 1)

    assert len(response.results) == 1
    headers = mock_post.call_args.kwargs["headers"]
    assert headers["Authorization"] == "Bearer config-api-key"


def test_empty_api_key_from_config_does_not_add_authorization_header() -> None:
    """Test empty ServiceConfig.rerank.apiKey does not add Authorization header."""
    reset_global_config()
    mock_config = gen_test_config(
        rerank={
            "endpoint": "http://config-endpoint",
            "modelServingName": "test-serving",
            "baseModelName": "bce-reranker-base",
            "apiKey": " ",
        }
    )

    with mock.patch.dict(os.environ, {"ServiceConfig": str(mock_config).replace("'", '"')}):
        service = create_rerank_service("bce-reranker-base")

    mock_response = mock.Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "results": [
            {
                "document": "doc1",
                "relevance_score": 0.9,
                "index": 0,
            }
        ]
    }
    with mock.patch("databuilder_logic_sdk.core.rerank.requests.post", return_value=mock_response) as mock_post:
        response = service.rerank("query", ["doc1"], 1)

    assert len(response.results) == 1
    headers = mock_post.call_args.kwargs["headers"]
    assert "Authorization" not in headers


def test_api_key_argument_has_priority_over_service_config() -> None:
    """Test explicit api_key argument has priority over ServiceConfig.rerank.apiKey."""
    reset_global_config()
    mock_config = gen_test_config(
        rerank={
            "endpoint": "http://config-endpoint",
            "modelServingName": "test-serving",
            "baseModelName": "bce-reranker-base",
            "apiKey": "config-api-key",
        }
    )

    with mock.patch.dict(os.environ, {"ServiceConfig": str(mock_config).replace("'", '"')}):
        service = create_rerank_service("bce-reranker-base", api_key="arg-api-key")

    mock_response = mock.Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "results": [
            {
                "document": "doc1",
                "relevance_score": 0.9,
                "index": 0,
            }
        ]
    }
    with mock.patch("databuilder_logic_sdk.core.rerank.requests.post", return_value=mock_response) as mock_post:
        response = service.rerank("query", ["doc1"], 1)

    assert len(response.results) == 1
    headers = mock_post.call_args.kwargs["headers"]
    assert headers["Authorization"] == "Bearer arg-api-key"


def test_wrong_base_name_in_config() -> None:
    """Test that create_rerank_service raises ValueError when model name does not match config base name."""
    reset_global_config()
    # Mock the environment variable for ServiceConfig
    mock_config = gen_test_config()
    with mock.patch.dict(os.environ, {"ServiceConfig": str(mock_config).replace("'", '"')}):
        with pytest.raises(ValueError, match="模型名称与配置base_name不匹配"):
            create_rerank_service("bce-reranker-base", api_key="test-api-key")


def test_create_rerank_service_unknown_model() -> None:
    """Test that create_rerank_service raises ValueError for unknown model."""
    with pytest.raises(ValueError, match="未知的模型"):
        create_rerank_service("unknown-model", api_key="test-key", endpoints=["http://test"])


def test_qianfan_reranker_init_no_endpoints() -> None:
    """Test QianFanReranker raises ValueError when no endpoints provided."""
    config = RerankConfig(model_name="bce-reranker-base", api_key="test-key", endpoints=[])
    with pytest.raises(ValueError, match="必须提供至少一个端点地址"):
        QianFanReranker(config)


def test_validate_and_adjust_params_empty_documents() -> None:
    """Test _validate_and_adjust_params raises ValueError for empty documents."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    with pytest.raises(ValueError, match="documents 不能为空"):
        service._validate_and_adjust_params([], 5)


def test_validate_and_adjust_params_top_k_greater_than_documents() -> None:
    """Test _validate_and_adjust_params adjusts top_k when greater than documents count."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    documents = ["doc1", "doc2", "doc3"]
    adjusted_top_k = service._validate_and_adjust_params(documents, 10)
    assert adjusted_top_k == 3


def test_validate_and_adjust_params_top_k_zero() -> None:
    """Test _validate_and_adjust_params adjusts top_k when zero."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    documents = ["doc1", "doc2"]
    adjusted_top_k = service._validate_and_adjust_params(documents, 0)
    assert adjusted_top_k == 2


def test_validate_and_adjust_params_top_k_negative() -> None:
    """Test _validate_and_adjust_params adjusts top_k when negative."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    documents = ["doc1", "doc2"]
    adjusted_top_k = service._validate_and_adjust_params(documents, -1)
    assert adjusted_top_k == 2


def test_validate_and_adjust_params_top_k_exceeds_max() -> None:
    """Test _validate_and_adjust_params truncates top_k when exceeds max_top_k."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"], max_top_k=10)
    documents = ["doc" + str(i) for i in range(20)]
    adjusted_top_k = service._validate_and_adjust_params(documents, 15)
    assert adjusted_top_k == 10


def test_validate_and_adjust_params_documents_exceeds_max() -> None:
    """Test _validate_and_adjust_params logs warning when documents exceed max_documents."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"], max_documents=5)
    documents = ["doc" + str(i) for i in range(10)]
    adjusted_top_k = service._validate_and_adjust_params(documents, 3)
    assert adjusted_top_k == 3


def test_validate_and_adjust_params_valid() -> None:
    """Test _validate_and_adjust_params with valid parameters."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    documents = ["doc1", "doc2", "doc3"]
    adjusted_top_k = service._validate_and_adjust_params(documents, 2)
    assert adjusted_top_k == 2


def test_sort() -> None:
    """Test sort method properly reorders objects based on rerank results."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    original_obj = ["obj1", "obj2", "obj3"]
    rerank_results = [
        RerankResult(document="doc2", relevance_score=0.9, index=1),
        RerankResult(document="doc3", relevance_score=0.8, index=2),
        RerankResult(document="doc1", relevance_score=0.7, index=0),
    ]
    sorted_obj = service.sort(original_obj, rerank_results)
    assert sorted_obj == ["obj2", "obj3", "obj1"]


def test_mock_rerank_method() -> None:
    """Test the rerank method with mocked response."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test"])
    query = "测试查询"
    documents = ["文档1", "文档2", "文档3"]

    with mock.patch.object(
        service,
        "rerank",
        return_value=RerankResponse(
            results=[
                RerankResult(document="文档2", relevance_score=0.95, index=1),
                RerankResult(document="文档1", relevance_score=0.85, index=0),
            ]
        ),
    ):
        response = service.rerank(query, documents, top_k=2)

    assert len(response.results) == 2
    assert response.results[0].document == "文档2"
    assert response.results[0].relevance_score == 0.95
    assert response.results[0].index == 1
    assert response.results[1].document == "文档1"
    assert response.results[1].relevance_score == 0.85
    assert response.results[1].index == 0


def test_qianfan_reranker_all_endpoints_fail() -> None:
    """Test that rerank raises RuntimeError when all endpoints fail."""
    service = create_rerank_service("bce-reranker-base", api_key="test-key", endpoints=["http://test1", "http://test2"])

    with mock.patch.object(service, "_call_rerank_single", side_effect=RuntimeError("API call failed")):
        with pytest.raises(RuntimeError, match="请求 Rerank 服务失败"):
            service.rerank("query", ["doc1", "doc2"], top_k=2)
