"""Tests for backend report PDF route."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock, patch

import httpx
import pytest
from backend.api.middleware.api_key_auth import APIKeyContext
from fastapi.testclient import TestClient


def _make_auth_context(
    features: list[str] | None = None,
    tier: str = "agency",
) -> APIKeyContext:
    if features is None:
        features = ["branded_pdf", "hosted_dashboard"]
    return APIKeyContext(
        customer_id="cust-1",
        email="admin@acme.com",
        tier=tier,
        features=features,
        project_limit=50,
        batch_limit=100,
    )


def _get_client() -> TestClient:
    from backend.api.main import app

    return TestClient(app)


@pytest.fixture
def _mock_gotenberg():
    """Mock Gotenberg returning a valid PDF."""
    mock_resp = MagicMock()
    mock_resp.status_code = 200
    mock_resp.content = b"%PDF-1.4 test"

    mock_client = AsyncMock()
    mock_client.post = AsyncMock(return_value=mock_resp)
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch(
        "backend.api.routes.report.httpx.AsyncClient",
        return_value=mock_client,
    ):
        yield mock_client


@pytest.fixture
def _mock_auth():
    """Mock API key auth returning Agency tier."""
    ctx = _make_auth_context()
    with patch(
        "backend.api.middleware.api_key_auth.require_api_key",
        new_callable=AsyncMock,
        return_value=ctx,
    ):
        yield


def test_generate_pdf_success(_mock_auth, _mock_gotenberg) -> None:
    """Mock Gotenberg — returns PDF bytes."""
    client = _get_client()
    resp = client.post(
        "/v1/report/pdf",
        json={"html": "<html><body>Report</body></html>"},
        headers={"Authorization": "Bearer test-key"},
    )
    assert resp.status_code == 200
    assert resp.headers["content-type"] == "application/pdf"
    assert resp.content == b"%PDF-1.4 test"


def test_generate_pdf_no_api_key() -> None:
    """Missing auth header returns 401."""
    client = _get_client()
    resp = client.post(
        "/v1/report/pdf",
        json={"html": "<html></html>"},
    )
    assert resp.status_code == 401


def test_generate_pdf_wrong_tier() -> None:
    """Non-agency tier returns 403."""
    ctx = _make_auth_context(features=["html_report"], tier="pro")
    with patch(
        "backend.api.middleware.api_key_auth.require_api_key",
        new_callable=AsyncMock,
        return_value=ctx,
    ):
        client = _get_client()
        resp = client.post(
            "/v1/report/pdf",
            json={"html": "<html></html>"},
            headers={"Authorization": "Bearer test-key"},
        )
    assert resp.status_code == 403
    assert "Agency tier" in resp.json()["detail"]


def test_generate_pdf_brand_injection(_mock_auth, _mock_gotenberg) -> None:
    """Brand config modifies HTML before Gotenberg."""
    client = _get_client()
    html = (
        "<html><head>"
        "<style>:root{--primary-color: #1a56db;}</style>"
        "<title>ComplyForm Compliance Report</title>"
        "</head><body>Content</body></html>"
    )
    resp = client.post(
        "/v1/report/pdf",
        json={
            "html": html,
            "brand": {
                "company_name": "Acme Corp",
                "primary_color": "#ff0000",
                "report_footer": "Acme Confidential",
            },
        },
        headers={"Authorization": "Bearer test-key"},
    )
    assert resp.status_code == 200

    # Verify the HTML sent to Gotenberg was modified
    call_kwargs = _mock_gotenberg.post.call_args
    files = call_kwargs.kwargs.get("files") or call_kwargs[1].get("files")
    sent_html = files["index.html"][1].decode("utf-8")
    assert "#ff0000" in sent_html
    assert "Acme Confidential" in sent_html


def test_generate_pdf_gotenberg_timeout(_mock_auth) -> None:
    """Gotenberg timeout returns 504."""
    mock_client = AsyncMock()
    mock_client.post = AsyncMock(
        side_effect=httpx.TimeoutException("timeout"),
    )
    mock_client.__aenter__ = AsyncMock(
        return_value=mock_client,
    )
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch(
        "backend.api.routes.report.httpx.AsyncClient",
        return_value=mock_client,
    ):
        client = _get_client()
        resp = client.post(
            "/v1/report/pdf",
            json={"html": "<html></html>"},
            headers={"Authorization": "Bearer test-key"},
        )
    assert resp.status_code == 504


def test_generate_pdf_gotenberg_error(_mock_auth) -> None:
    """Gotenberg 500 returns 502."""
    mock_resp = MagicMock()
    mock_resp.status_code = 500
    mock_resp.text = "Internal error"

    mock_client = AsyncMock()
    mock_client.post = AsyncMock(return_value=mock_resp)
    mock_client.__aenter__ = AsyncMock(
        return_value=mock_client,
    )
    mock_client.__aexit__ = AsyncMock(return_value=False)

    with patch(
        "backend.api.routes.report.httpx.AsyncClient",
        return_value=mock_client,
    ):
        client = _get_client()
        resp = client.post(
            "/v1/report/pdf",
            json={"html": "<html></html>"},
            headers={"Authorization": "Bearer test-key"},
        )
    assert resp.status_code == 502


def test_generate_pdf_payload_too_large(_mock_auth) -> None:
    """HTML > 10MB returns 413."""
    client = _get_client()
    huge_html = "x" * (11 * 1024 * 1024)
    resp = client.post(
        "/v1/report/pdf",
        json={"html": huge_html},
        headers={"Authorization": "Bearer test-key"},
    )
    assert resp.status_code == 413
