"""Q3-F 标签聚合与按标签筛选的接口契约测试。"""

from __future__ import annotations

import pytest


def auth(token: str) -> dict[str, str]:
    return {"Authorization": f"Bearer {token}"}


def data(response):
    return response.get_json()["data"]


def names_of(items):
    return {item["documentName"] for item in items}


def test_tags_aggregation_requires_auth(b4_client):
    response = b4_client.get("/api/v1/documents/tags")
    assert response.status_code == 401
    assert response.get_json()["code"] == "TOKEN_INVALID"


def test_tags_aggregation_dedups_main_plan_tags(b4_client, token_for):
    response = b4_client.get(
        "/api/v1/documents/tags", headers=auth(token_for("admin"))
    )
    assert response.status_code == 200
    payload = data(response)
    assert payload["items"] == ["B4", "测试"]
    assert payload["total"] == 2


def test_tags_aggregation_respects_visibility(b4_client, token_for):
    """普通用户看不到绝密/草稿/无授权主案，但可见主案的标签仍可聚合。"""
    response = b4_client.get(
        "/api/v1/documents/tags", headers=auth(token_for("user"))
    )
    assert response.status_code == 200
    payload = data(response)
    # B4 系列可见主案的标签都是 ["B4", "测试"]，去重后稳定为这两项。
    assert payload["items"] == ["B4", "测试"]


def test_documents_filter_by_single_tag(b4_client, token_for):
    response = b4_client.get(
        "/api/v1/documents?tags=B4&pageSize=100",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 200
    items = data(response)["items"]
    expected_visible_main_plans = {
        "B4_TEST_全部主案",
        "B4_TEST_组织主案",
        "B4_TEST_自定义主案",
        "B4_TEST_草稿主案",
        "B4_TEST_绝密主案",
        "B4_TEST_无授权主案",
    }
    assert items and names_of(items) == expected_visible_main_plans


def test_documents_filter_by_nonexistent_tag_returns_empty(
    b4_client, token_for
):
    response = b4_client.get(
        "/api/v1/documents?tags=不存在的标签",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 200
    payload = data(response)
    assert payload["items"] == []
    assert payload["total"] == 0


def test_documents_tag_filter_or_semantics(b4_client, token_for):
    """默认 tagsMatch=any：多标签任一命中即返回。"""
    response = b4_client.get(
        "/api/v1/documents?tags=B4,不存在的&pageSize=100",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 200
    items = data(response)["items"]
    assert items and all("B4_TEST_" in item["documentName"] for item in items)


def test_documents_tag_filter_all_semantics(b4_client, token_for):
    """tagsMatch=all：所有标签都必须命中。B4/测试 同时存在 → 全部主案。"""
    response = b4_client.get(
        "/api/v1/documents?tags=B4,测试&tagsMatch=all&pageSize=100",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 200
    items = data(response)["items"]
    assert items and {item["tags"] for item in items} == {["B4", "测试"]}


def test_documents_tag_filter_all_with_missing_excludes(
    b4_client, token_for
):
    response = b4_client.get(
        "/api/v1/documents?tags=B4,缺失&tagsMatch=all&pageSize=100",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 200
    payload = data(response)
    assert payload["items"] == []
    assert payload["total"] == 0


@pytest.mark.parametrize("query", ["tagsMatch=invalid", "tagsMatch=ANY"])
def test_documents_invalid_tags_match_rejected(b4_client, token_for, query):
    response = b4_client.get(
        f"/api/v1/documents?tags=B4&{query}",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 400
    assert response.get_json()["code"] == "INVALID_ARGUMENT"


def test_documents_tag_filter_excludes_sub_plans(b4_client, token_for):
    """标签筛选只命中主案，子方案/附件不在结果中。"""
    response = b4_client.get(
        "/api/v1/documents?tags=B4&pageSize=100",
        headers=auth(token_for("admin")),
    )
    assert response.status_code == 200
    types = {item["documentType"] for item in data(response)["items"]}
    assert types == {"MAIN"}
