"""
Tests pour POST /api/v1/translate
Couvre les AC 1-10 de la story 2.10 : Endpoint POST /api/v1/translate (Core)
"""

import io
import time
from pathlib import Path
from unittest.mock import patch, MagicMock, AsyncMock
from zipfile import ZipFile

import pytest
from fastapi.testclient import TestClient

TRANSLATE_URL = "/api/v1/translate"
STATUS_URL = "/api/v1/translations"
REGISTER_URL = "/api/v1/auth/register"
LOGIN_URL = "/api/v1/auth/login"

VALID_USER = {
    "email": "translate@example.com",
    "password": "Password123!",
    "name": "Translate User",
}


def create_valid_excel() -> bytes:
    """Create a minimal valid .xlsx file (ZIP with office content)."""
    buf = io.BytesIO()
    with ZipFile(buf, "w") as zf:
        zf.writestr(
            "[Content_Types].xml",
            '<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"></Types>',
        )
        zf.writestr(
            "_rels/.rels",
            '<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>',
        )
        zf.writestr(
            "xl/workbook.xml",
            '<?xml version="1.0"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"></workbook>',
        )
    buf.seek(0)
    return buf.read()


def create_valid_docx() -> bytes:
    """Create a minimal valid .docx file."""
    buf = io.BytesIO()
    with ZipFile(buf, "w") as zf:
        zf.writestr(
            "[Content_Types].xml",
            '<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"></Types>',
        )
        zf.writestr(
            "_rels/.rels",
            '<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>',
        )
        zf.writestr(
            "word/document.xml",
            '<?xml version="1.0"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"></w:document>',
        )
    buf.seek(0)
    return buf.read()


def create_valid_pptx() -> bytes:
    """Create a minimal valid .pptx file."""
    buf = io.BytesIO()
    with ZipFile(buf, "w") as zf:
        zf.writestr(
            "[Content_Types].xml",
            '<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"></Types>',
        )
        zf.writestr(
            "_rels/.rels",
            '<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>',
        )
        zf.writestr(
            "ppt/presentation.xml",
            '<?xml version="1.0"?><p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"></p:presentation>',
        )
    buf.seek(0)
    return buf.read()


def create_invalid_file() -> bytes:
    """Create an invalid file (not a ZIP/Office document)."""
    return b"This is not a valid office document"


@pytest.fixture()
def users_file(tmp_path: Path) -> Path:
    """Fichier de stockage JSON isole pour les tests."""
    return tmp_path / "users.json"


@pytest.fixture()
def client(users_file: Path, monkeypatch):
    """TestClient avec stockage JSON isole et rate limiting desactive."""
    import services.auth_service as auth_svc

    monkeypatch.setattr(auth_svc, "USERS_FILE", users_file)
    monkeypatch.setattr(auth_svc, "USE_DATABASE", False)
    monkeypatch.setattr(auth_svc, "DATABASE_AVAILABLE", False)

    from middleware.rate_limiting import RateLimitManager

    async def _check_request_allow(self, request):
        return True, "ok", "test"

    async def _check_translation_allow(self, request, file_size_mb=0):
        return True, "ok"

    monkeypatch.setattr(RateLimitManager, "check_request", _check_request_allow)
    monkeypatch.setattr(RateLimitManager, "check_translation", _check_translation_allow)

    def _check_usage_limits_allow(user):
        return {
            "can_translate": True,
            "docs_used": 0,
            "docs_limit": 5,
            "docs_remaining": 5,
            "pages_used": 0,
            "extra_credits": 0,
            "max_pages_per_doc": 50,
            "max_file_size_mb": 10,
            "allowed_providers": ["google"],
        }

    monkeypatch.setattr(
        "routes.translate_routes.check_usage_limits", _check_usage_limits_allow
    )

    from main import app

    return TestClient(app, raise_server_exceptions=True)


@pytest.fixture()
def authenticated_client(client):
    """Client avec un utilisateur enregistre et authentifie."""
    client.post(REGISTER_URL, json=VALID_USER)
    response = client.post(
        LOGIN_URL,
        json={
            "email": VALID_USER["email"],
            "password": VALID_USER["password"],
        },
    )
    token = response.json()["data"]["access_token"]
    client.headers["Authorization"] = f"Bearer {token}"
    return client


# ---------------------------------------------------------------------------
# AC2: File Upload
# ---------------------------------------------------------------------------


class TestFileUpload:
    """AC2: POST to /api/v1/translate accepts multipart/form-data"""

    def test_accepts_multipart_form_data(self, authenticated_client):
        """Endpoint accepts multipart/form-data with file, source_lang, target_lang"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202

    def test_requires_file_or_url(self, client):
        """Returns error if neither file nor file_url provided"""
        response = client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr"},
        )
        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "MISSING_FILE"

    def test_accepts_source_and_target_lang(self, authenticated_client):
        """Accepts source_lang and target_lang parameters"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"source_lang": "en", "target_lang": "fr"},
        )
        assert response.status_code == 202
        body = response.json()
        assert body["data"]["source_lang"] == "en"
        assert body["data"]["target_lang"] == "fr"


# ---------------------------------------------------------------------------
# AC3 & AC5: File Validation
# ---------------------------------------------------------------------------


class TestFileValidation:
    """AC3, AC5: System validates format (xlsx/docx/pptx only), max size 50MB, magic bytes"""

    def test_rejects_invalid_format_pdf(self, authenticated_client):
        """AC5: Unsupported formats return 400 with INVALID_FORMAT"""
        invalid_content = create_invalid_file()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": ("test.txt", io.BytesIO(invalid_content), "text/plain")
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "INVALID_FORMAT"

    def test_rejects_invalid_magic_bytes(self, authenticated_client):
        """AC3/AC4: Checks magic bytes, returns CORRUPTED_FILE for invalid content"""
        invalid_content = create_invalid_file()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "fake.xlsx",
                    io.BytesIO(invalid_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "CORRUPTED_FILE"

    def test_accepts_xlsx(self, authenticated_client):
        """Accepts .xlsx files"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202

    def test_accepts_docx(self, authenticated_client):
        """Accepts .docx files"""
        docx_content = create_valid_docx()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.docx",
                    io.BytesIO(docx_content),
                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202

    def test_accepts_pptx(self, authenticated_client):
        """Accepts .pptx files"""
        pptx_content = create_valid_pptx()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.pptx",
                    io.BytesIO(pptx_content),
                    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202

    def test_error_includes_accepted_formats(self, authenticated_client):
        """AC5: Error includes accepted formats list"""
        invalid_content = create_invalid_file()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={"file": ("test.txt", io.BytesIO(invalid_content), "text/plain")},
            data={"target_lang": "fr"},
        )
        body = response.json()
        assert "accepted_formats" in body.get("details", {}) or ".xlsx" in str(body)


# ---------------------------------------------------------------------------
# AC7: File Too Large
# ---------------------------------------------------------------------------


class TestFileTooLarge:
    """AC7: Files > 50MB return 413 with FILE_TOO_LARGE"""

    def test_returns_413_for_large_file(self, authenticated_client, monkeypatch):
        """Files exceeding max size return 413"""
        from middleware.validation import FileValidator

        # Create a validator with very small limit for testing
        small_validator = FileValidator(
            max_size_mb=0.001, allowed_extensions={".xlsx", ".docx", ".pptx"}
        )
        monkeypatch.setattr("routes.translate_routes.file_validator", small_validator)

        large_content = b"x" * 2000  # 2KB > 1KB limit
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "large.xlsx",
                    io.BytesIO(large_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 413
        body = response.json()
        assert body["error"] == "FILE_TOO_LARGE"


# ---------------------------------------------------------------------------
# AC4: Success Response
# ---------------------------------------------------------------------------


class TestSuccessResponse:
    """AC4: Valid requests return HTTP 202 with proper format"""

    def test_returns_202_on_success(self, authenticated_client):
        """Returns HTTP 202 Accepted"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202

    def test_response_has_data_with_id(self, authenticated_client):
        """Response contains data.id"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        body = response.json()
        assert "data" in body
        assert "id" in body["data"]
        assert body["data"]["id"].startswith("tr_")

    def test_response_has_status_processing(self, authenticated_client):
        """Response contains status 'processing'"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        body = response.json()
        assert body["data"]["status"] == "processing"

    def test_response_has_meta_with_rate_limit(self, authenticated_client):
        """Response contains meta.rate_limit_remaining"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        body = response.json()
        assert "meta" in body
        assert "rate_limit_remaining" in body["meta"]


# ---------------------------------------------------------------------------
# AC1: Authentication
# ---------------------------------------------------------------------------


class TestAuthentication:
    """AC1: Endpoint requires valid JWT token or X-API-Key"""

    def test_works_with_jwt_token(self, authenticated_client):
        """Accepts JWT Bearer token"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202

    def test_works_without_auth(self, client):
        """Allows unauthenticated requests (but with tier limits)"""
        excel_content = create_valid_excel()
        response = client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        # Should still work without auth
        assert response.status_code == 202


# ---------------------------------------------------------------------------
# AC6: Quota Exceeded
# ---------------------------------------------------------------------------


class TestQuotaExceeded:
    """AC6: Users exceeding tier limit return 429"""

    def test_returns_429_when_quota_exceeded(self, client, monkeypatch):
        """Returns 429 with QUOTA_EXCEEDED when quota exceeded"""
        def _check_usage_limits_denied(user):
            return {
                "can_translate": False,
                "docs_used": 5,
                "docs_limit": 5,
                "docs_remaining": 0,
                "pages_used": 0,
                "extra_credits": 0,
                "max_pages_per_doc": 50,
                "max_file_size_mb": 10,
                "allowed_providers": ["google"],
            }

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits", _check_usage_limits_denied
        )

        # Register and login
        client.post(REGISTER_URL, json=VALID_USER)
        response = client.post(
            LOGIN_URL,
            json={
                "email": VALID_USER["email"],
                "password": VALID_USER["password"],
            },
        )
        token = response.json()["data"]["access_token"]
        client.headers["Authorization"] = f"Bearer {token}"

        excel_content = create_valid_excel()
        response = client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 429
        body = response.json()
        # HTTPException returns detail dict with error field
        assert (
            body.get("error") == "QUOTA_EXCEEDED"
            or body.get("detail", {}).get("error") == "QUOTA_EXCEEDED"
        )

    def test_includes_retry_after_header(self, client, monkeypatch):
        """Includes Retry-After header on 429"""

        def _check_usage_limits_denied(user):
            return {
                "can_translate": False,
                "docs_used": 5,
                "docs_limit": 5,
                "docs_remaining": 0,
                "pages_used": 0,
                "extra_credits": 0,
                "max_pages_per_doc": 50,
                "max_file_size_mb": 10,
                "allowed_providers": ["google"],
            }

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits", _check_usage_limits_denied
        )

        client.post(REGISTER_URL, json=VALID_USER)
        response = client.post(
            LOGIN_URL,
            json={
                "email": VALID_USER["email"],
                "password": VALID_USER["password"],
            },
        )
        token = response.json()["data"]["access_token"]
        client.headers["Authorization"] = f"Bearer {token}"

        excel_content = create_valid_excel()
        response = client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert "retry-after" in response.headers


# ---------------------------------------------------------------------------
# AC10: Optional Parameters
# ---------------------------------------------------------------------------


class TestOptionalParameters:
    """AC10: Support mode, provider, webhook_url, glossary_id, custom_prompt"""

    def test_accepts_mode_classic(self, authenticated_client):
        """Accepts mode='classic'"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "mode": "classic"},
        )
        assert response.status_code == 202

    def test_accepts_mode_llm(self, authenticated_client):
        """mode='llm' maps to the openrouter engine — Pro and above only.

        The default test user is on the Free plan, so the plan-based engine
        gate must refuse it (403) instead of silently running a paid engine.
        """
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "mode": "llm"},
        )
        assert response.status_code == 403
        assert "PRO_FEATURE_REQUIRED" in str(response.json())

    def test_accepts_webhook_url(self, authenticated_client):
        """Accepts webhook_url parameter"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "webhook_url": "https://example.com/webhook"},
        )
        assert response.status_code == 202


# ---------------------------------------------------------------------------
# AC8: Async Processing
# ---------------------------------------------------------------------------


class TestAsyncProcessing:
    """AC8: Translation is processed asynchronously"""

    def test_returns_immediately_with_job_id(self, authenticated_client):
        """Endpoint returns 202 immediately with job ID"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        assert response.status_code == 202
        body = response.json()
        assert body["data"]["status"] == "processing"
        assert body["data"]["id"].startswith("tr_")

    def test_can_check_job_status(self, authenticated_client):
        """Can check job status via GET /api/v1/translations/{id}"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
        )
        job_id = response.json()["data"]["id"]

        status_response = authenticated_client.get(f"{STATUS_URL}/{job_id}")
        assert status_response.status_code == 200
        body = status_response.json()
        assert "data" in body
        assert body["data"]["id"] == job_id

    def test_returns_404_for_unknown_job(self, authenticated_client):
        """Returns 404 for unknown job ID"""
        response = authenticated_client.get(f"{STATUS_URL}/tr_unknown123")
        assert response.status_code == 404


# ---------------------------------------------------------------------------
# AC9: URL Ingestion (Pro)
# ---------------------------------------------------------------------------


class TestURLIngestion:
    """AC9: Pro users can provide file_url parameter instead of file upload"""

    def test_pro_feature_requires_pro_tier(self, authenticated_client, monkeypatch):
        """file_url is a Pro-only feature"""
        from models.subscription import User, PlanType
        from datetime import datetime

        # User is free tier by default, should get 403
        excel_url = "https://example.com/test.xlsx"
        response = authenticated_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": excel_url},
        )
        assert response.status_code == 403
        body = response.json()
        assert body["error"] == "PRO_FEATURE_REQUIRED"

    def test_glossary_requires_pro(self, authenticated_client):
        """glossary_id is a Pro-only feature"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "glossary_id": "some-glossary-id"},
        )
        assert response.status_code == 403
        body = response.json()
        assert body["error"] == "PRO_FEATURE_REQUIRED"

    def test_custom_prompt_requires_pro(self, authenticated_client):
        """custom_prompt is a Pro-only feature"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "custom_prompt": "Translate formally"},
        )
        assert response.status_code == 403
        body = response.json()
        assert body["error"] == "PRO_FEATURE_REQUIRED"


# ---------------------------------------------------------------------------
# Additional Tests for Code Review Issues
# ---------------------------------------------------------------------------


class TestProviderParameter:
    """AC10: Provider parameter support"""

    def test_accepts_provider_google(self, authenticated_client):
        """Accepts provider='google'"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "provider": "google"},
        )
        assert response.status_code == 202



class TestSourceLangValidation:
    """Source language validation"""

    def test_invalid_source_lang_returns_400(self, authenticated_client):
        """Invalid source_lang returns 400"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "source_lang": "invalid_code_xyz"},
        )
        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "INVALID_FORMAT"


class TestWebhookValidation:
    """Webhook URL validation"""

    def test_invalid_webhook_url_returns_400(self, authenticated_client):
        """Invalid webhook_url returns 400"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "webhook_url": "not-a-valid-url"},
        )
        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "INVALID_WEBHOOK_URL"

    def test_valid_webhook_url_accepted(self, authenticated_client):
        """Valid webhook_url is accepted"""
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "webhook_url": "https://example.com/webhook"},
        )
        assert response.status_code == 202


class TestNoHTTP500:
    """NFR12: Zero HTTP 500 - all errors should be 4xx"""

    def test_unexpected_error_returns_400_not_500(
        self, authenticated_client, monkeypatch
    ):
        """Unexpected errors return 400, not 500"""
        from routes import translate_routes

        async def _failing_validate(*args, **kwargs):
            raise RuntimeError("Unexpected error")

        monkeypatch.setattr(
            translate_routes.file_validator, "validate_async", _failing_validate
        )

        response = authenticated_client.post(
            TRANSLATE_URL,
            files={"file": ("test.xlsx", io.BytesIO(b"fake"), "application/vnd...")},
            data={"target_lang": "fr"},
        )
        assert response.status_code in [400, 413, 401, 403, 429]


class TestAPIKeyAuth:
    """AC1: X-API-Key header authentication"""

    def test_api_key_auth_placeholder(self, client):
        """X-API-Key header is accepted (placeholder test)"""
        excel_content = create_valid_excel()
        response = client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr"},
            headers={"X-API-Key": "test-api-key-placeholder"},
        )
        assert response.status_code in [202, 401]


class TestTranslateImagesParameter:
    """Test translate_images parameter in POST /api/v1/translate"""

    def test_accepts_translate_images_parameter(self, authenticated_client):
        """translate_images is a Pro+ feature — Free plan gets 403.

        The parameter is accepted by the schema; the plan gate refuses it
        for the default (Free) test user.
        """
        excel_content = create_valid_excel()
        response = authenticated_client.post(
            TRANSLATE_URL,
            files={
                "file": (
                    "test.xlsx",
                    io.BytesIO(excel_content),
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                )
            },
            data={"target_lang": "fr", "translate_images": "true"},
        )
        assert response.status_code == 403
        assert "PRO_FEATURE_REQUIRED" in str(response.json())
