from __future__ import annotations

import io
import json
from urllib.parse import quote

from dms.api.v1.blueprint import _merge_exposed_headers

ORIGIN = "http://127.0.0.1:9346"
EXPOSED = {"content-disposition", "x-request-id"}


def _headers(token: str | None = None) -> dict[str, str]:
    headers = {"Origin": ORIGIN}
    if token:
        headers["Authorization"] = f"Bearer {token}"
    return headers


def _exposed(response) -> list[str]:
    return [
        value.strip().lower()
        for value in response.headers["Access-Control-Expose-Headers"].split(",")
        if value.strip()
    ]


def test_dms_json_success_exposes_headers_and_request_id_matches(client):
    response = client.get("/api/v1/health", headers=_headers())
    payload = response.get_json()
    assert response.headers["Access-Control-Allow-Origin"] == ORIGIN
    assert EXPOSED <= set(_exposed(response))
    assert "*" not in _exposed(response)
    assert response.headers["X-Request-Id"] == payload["requestId"]


def test_dms_401_and_routing_404_expose_request_id(client):
    unauthorized = client.get("/api/v1/documents", headers=_headers())
    assert unauthorized.status_code == 401
    assert EXPOSED <= set(_exposed(unauthorized))
    assert (
        unauthorized.headers["X-Request-Id"]
        == unauthorized.get_json()["requestId"]
    )
    missing = client.get("/api/v1/not-implemented", headers=_headers())
    assert missing.status_code == 404
    assert EXPOSED <= set(_exposed(missing))
    assert missing.headers["X-Request-Id"] == missing.get_json()["requestId"]


def test_expose_header_merge_is_case_insensitive_and_deduplicated():
    value = _merge_exposed_headers(
        "ETag, x-request-id, etag, CONTENT-DISPOSITION"
    )
    parts = [item.strip().lower() for item in value.split(",")]
    assert parts == ["etag", "x-request-id", "content-disposition"]
    assert len(parts) == len(set(parts))
    assert "*" not in parts


def test_preview_and_chinese_download_expose_headers_once(
    b5_app, b5_client, token_for
):
    from dms.extensions import db
    from dms.models import AuditLog, Document

    token = token_for("admin")
    filename = "中文下载文件.pdf"
    created = b5_client.post(
        "/api/v1/attachments",
        data={
            "file": (io.BytesIO(b"%PDF-1.4\nCORS"), filename),
            "metadata": json.dumps(
                {
                    "documentName": "B5_C1_TEST_中文下载",
                    "attachmentType": "POLICY",
                    "summary": "CORS响应头验证",
                    "tags": ["CORS"],
                },
                ensure_ascii=False,
            ),
        },
        headers=_headers(token),
        content_type="multipart/form-data",
    ).get_json()["data"]
    preview = b5_client.get(
        f"/api/v1/documents/{created['id']}/preview",
        headers=_headers(token),
    )
    assert preview.status_code == 200
    assert EXPOSED <= set(_exposed(preview))
    assert preview.headers["Content-Disposition"].startswith("inline")
    preview.close()

    download = b5_client.get(
        f"/api/v1/documents/{created['id']}/download",
        headers=_headers(token),
    )
    disposition = download.headers["Content-Disposition"]
    assert download.status_code == 200
    assert download.headers["Access-Control-Allow-Origin"] == ORIGIN
    assert EXPOSED <= set(_exposed(download))
    assert "filename=" in disposition
    assert "filename*=UTF-8''" in disposition
    assert quote(filename, safe="") in disposition
    assert download.headers["X-Request-Id"]
    download.close()
    with b5_app.app_context():
        document = db.session.get(Document, int(created["id"]))
        audit_count = db.session.scalar(
            db.select(db.func.count(AuditLog.id)).where(
                AuditLog.target_id == document.id,
                AuditLog.action_type == "DOWNLOAD_DOCUMENT",
            )
        )
        assert document.download_count == 1
        assert audit_count == 1


def test_ai_response_does_not_gain_dms_expose_headers(client):
    response = client.get("/api/health", headers=_headers())
    assert response.status_code == 200
    assert response.headers["Access-Control-Allow-Origin"] == ORIGIN
    assert "Access-Control-Expose-Headers" not in response.headers
