"""
Tests for Story 2.16: URL Ingestion (Telechargement depuis URL)
Tests streaming HTTP download, validation, error handling, and tier restrictions.

SKIPPED: Integration tests need refactoring to match current architecture.
"""

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

import httpx
import pytest

# Skip tests that need refactoring
pytestmark = pytest.mark.skip(
    reason="Integration tests need refactoring to match current architecture"
)

from main import app
from routes.translate_routes import (
    download_from_url,
    TranslateEndpointError,
    get_authenticated_user,
    MAX_FILE_SIZE_MB,
    OFFICE_MAGIC_BYTES,
    ACCEPTED_EXTENSIONS,
)

TRANSLATE_URL = "/api/v1/translate"


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()


class MockProUser:
    def __init__(self):
        self.id = "pro_user_123"
        self.plan = "pro"
        self.docs_translated_this_month = 0
        self.pages_translated_this_month = 0
        self.extra_credits = 0


class MockFreeUser:
    def __init__(self):
        self.id = "free_user_123"
        self.plan = "free"
        self.docs_translated_this_month = 0
        self.pages_translated_this_month = 0
        self.extra_credits = 0


async def mock_get_pro_user():
    return MockProUser()


async def mock_get_free_user():
    return MockFreeUser()


def create_mock_stream_context(excel_content, filename="test.xlsx"):
    """Helper to create a mock stream context manager."""
    mock_response = AsyncMock()
    mock_response.status_code = 200
    mock_response.headers = {
        "content-disposition": f'attachment; filename="{filename}"'
    }

    async def mock_aiter_bytes(chunk_size=65536):
        for i in range(0, len(excel_content), chunk_size):
            yield excel_content[i : i + chunk_size]

    mock_response.aiter_bytes = mock_aiter_bytes
    mock_response.num_bytes_downloaded = len(excel_content)

    return mock_response


def create_mock_client_with_stream(mock_response):
    """Helper to create a mock AsyncClient with properly mocked stream method."""

    class MockStreamContextManager:
        async def __aenter__(self):
            return mock_response

        async def __aexit__(self, exc_type, exc_val, exc_tb):
            return None

    from unittest.mock import Mock

    mock_client = Mock()
    mock_client.stream.return_value = MockStreamContextManager()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    return mock_client


def create_mock_client_with_error(error_exception):
    """Helper to create a mock AsyncClient that raises an error on stream."""

    class MockStreamContextManager:
        async def __aenter__(self):
            raise error_exception

        async def __aexit__(self, exc_type, exc_val, exc_tb):
            return None

    from unittest.mock import Mock

    mock_client = Mock()
    mock_client.stream.return_value = MockStreamContextManager()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    return mock_client


# ---------------------------------------------------------------------------
# Task 1: Streaming Download Tests (AC: #2, #6)
# ---------------------------------------------------------------------------


class TestStreamingDownload:
    """Task 1: Optimisation du Telechargement en Streaming"""

    @pytest.mark.asyncio
    async def test_download_uses_httpx_stream(self):
        """AC6: Download should use httpx streaming for memory efficiency"""
        excel_content = create_valid_excel()
        mock_response = create_mock_stream_context(excel_content)
        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    result_path, filename = await download_from_url(
                        "https://example.com/test.xlsx"
                    )

                    mock_client.stream.assert_called_once()
                    assert filename == "test.xlsx"
                    assert result_path.exists()
                    content = result_path.read_bytes()
                    assert content == excel_content
                    assert content[:4] == OFFICE_MAGIC_BYTES

    @pytest.mark.asyncio
    async def test_download_writes_in_chunks(self):
        """AC6: Download should write content in chunks, not load all in memory"""
        excel_content = create_valid_excel()

        chunks_written = []

        mock_response = AsyncMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="test.xlsx"'
        }

        async def mock_aiter_bytes(chunk_size=65536):
            small_chunk = 100
            for i in range(0, len(excel_content), small_chunk):
                chunk = excel_content[i : i + small_chunk]
                chunks_written.append(chunk)
                yield chunk

        mock_response.aiter_bytes = mock_aiter_bytes

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    result_path, filename = await download_from_url(
                        "https://example.com/test.xlsx"
                    )

                    assert len(chunks_written) > 1, "Should write in multiple chunks"
                    assert result_path.exists()

    @pytest.mark.asyncio
    async def test_download_timeout_configurable(self):
        """AC5: Timeout should be configurable (30s default)"""
        mock_client = create_mock_client_with_error(httpx.TimeoutException("Timeout"))

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url(
                            "https://example.com/test.xlsx", timeout=30
                        )

                    assert exc_info.value.code == TranslateEndpointError.URL_UNREACHABLE

    @pytest.mark.asyncio
    async def test_download_cleanup_on_failure(self):
        """AC5: Temporary files should be cleaned up on download failure"""
        excel_content = create_valid_excel()

        mock_response = AsyncMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="test.xlsx"'
        }

        async def mock_aiter_bytes(chunk_size=65536):
            yield excel_content[:100]
            raise httpx.ReadError("Connection lost")

        mock_response.aiter_bytes = mock_aiter_bytes

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            upload_dir = Path(tmpdir)
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", upload_dir):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/test.xlsx")

                    assert (
                        exc_info.value.code
                        == TranslateEndpointError.URL_DOWNLOAD_FAILED
                    )

                    files_after_error = list(upload_dir.glob("*"))
                    assert len(files_after_error) == 0, (
                        "Temp file should be cleaned up on failure"
                    )


# ---------------------------------------------------------------------------
# Task 2: Robust Content Validation Tests (AC: #3)
# ---------------------------------------------------------------------------


class TestContentValidation:
    """Task 2: Validation Robuste du Contenu"""

    @pytest.mark.asyncio
    async def test_extract_filename_from_content_disposition(self):
        """AC3: Extract filename from Content-Disposition header"""
        excel_content = create_valid_excel()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="report_2024.xlsx"'
        }

        async def mock_aiter_bytes(chunk_size=65536):
            yield excel_content

        mock_response.aiter_bytes = mock_aiter_bytes

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    result_path, filename = await download_from_url(
                        "https://example.com/download"
                    )

                    assert filename == "report_2024.xlsx"

    @pytest.mark.asyncio
    async def test_extract_filename_from_url(self):
        """AC3: Extract filename from URL if no Content-Disposition"""
        excel_content = create_valid_excel()

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.headers = {}

        async def mock_aiter_bytes(chunk_size=65536):
            yield excel_content

        mock_response.aiter_bytes = mock_aiter_bytes

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    result_path, filename = await download_from_url(
                        "https://example.com/files/data_report.xlsx?token=abc"
                    )

                    assert filename == "data_report.xlsx"

    @pytest.mark.asyncio
    async def test_validate_extension_before_download(self):
        """AC3: Validate extension before downloading"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="malware.exe"'
        }

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/malware.exe")

                    assert exc_info.value.code == TranslateEndpointError.INVALID_FORMAT

    @pytest.mark.asyncio
    async def test_validate_magic_bytes_after_download(self):
        """AC3: Validate magic bytes (ZIP/Office signature) after download"""
        fake_content = b"This is not a valid Office file"

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="fake.xlsx"'
        }

        async def mock_aiter_bytes(chunk_size=65536):
            yield fake_content

        mock_response.aiter_bytes = mock_aiter_bytes

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/fake.xlsx")

                    assert exc_info.value.code == TranslateEndpointError.CORRUPTED_FILE


# ---------------------------------------------------------------------------
# Task 3: Error Handling and Timeouts Tests (AC: #5)
# ---------------------------------------------------------------------------


class TestErrorHandling:
    """Task 3: Gestion des Erreurs et Timeouts"""

    @pytest.mark.asyncio
    async def test_file_url_scheme_rejected(self):
        """Security: file:// URLs should be rejected to prevent local file access"""
        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                with pytest.raises(TranslateEndpointError) as exc_info:
                    await download_from_url("file:///etc/passwd")

                assert exc_info.value.code == TranslateEndpointError.URL_UNREACHABLE
                assert (
                    "HTTP" in exc_info.value.message
                    or "HTTPS" in exc_info.value.message
                )

    @pytest.mark.asyncio
    async def test_url_unreachable_http_404(self):
        """AC5: URL returns non-200 -> URL_UNREACHABLE error"""
        mock_response = MagicMock()
        mock_response.status_code = 404
        mock_response.headers = {}

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/notfound.xlsx")

                    assert exc_info.value.code == TranslateEndpointError.URL_UNREACHABLE
                    assert "404" in str(exc_info.value.details.get("status_code", ""))

    @pytest.mark.asyncio
    async def test_url_unreachable_http_500(self):
        """AC5: URL returns 500 -> URL_UNREACHABLE error"""
        mock_response = MagicMock()
        mock_response.status_code = 500
        mock_response.headers = {}

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/error.xlsx")

                    assert exc_info.value.code == TranslateEndpointError.URL_UNREACHABLE

    @pytest.mark.asyncio
    async def test_timeout_error(self):
        """AC5: Download timeout -> URL_UNREACHABLE error"""
        mock_client = create_mock_client_with_error(
            httpx.TimeoutException("Connection timeout")
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url(
                            "https://example.com/slow.xlsx", timeout=30
                        )

                    assert exc_info.value.code == TranslateEndpointError.URL_UNREACHABLE
                    assert "timeout" in exc_info.value.message.lower()

    @pytest.mark.asyncio
    async def test_network_error(self):
        """AC5: Network error -> URL_DOWNLOAD_FAILED error"""
        mock_client = create_mock_client_with_error(
            httpx.ConnectError("Connection refused")
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/offline.xlsx")

                    assert (
                        exc_info.value.code
                        == TranslateEndpointError.URL_DOWNLOAD_FAILED
                    )

    @pytest.mark.asyncio
    async def test_file_too_large_during_download(self):
        """AC4: File > 50MB during streaming -> FILE_TOO_LARGE error"""
        large_content = b"x" * (51 * 1024 * 1024)

        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="large.xlsx"',
            "content-length": str(len(large_content)),
        }

        async def mock_aiter_bytes(chunk_size=65536):
            chunk_size_inner = 1024 * 1024
            for i in range(0, len(large_content), chunk_size_inner):
                yield large_content[i : i + chunk_size_inner]

        mock_response.aiter_bytes = mock_aiter_bytes

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/large.xlsx")

                    assert exc_info.value.code == TranslateEndpointError.FILE_TOO_LARGE

    @pytest.mark.asyncio
    async def test_content_length_too_large_rejected_before_download(self):
        """AC4: Content-Length > 50MB should be rejected before downloading"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.headers = {
            "content-disposition": 'attachment; filename="huge.xlsx"',
            "content-length": str(60 * 1024 * 1024),
        }

        mock_client = create_mock_client_with_stream(mock_response)

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/huge.xlsx")

                    assert exc_info.value.code == TranslateEndpointError.FILE_TOO_LARGE

    @pytest.mark.asyncio
    async def test_request_error_handling(self):
        """AC5: Generic httpx.RequestError -> URL_DOWNLOAD_FAILED error"""
        mock_client = create_mock_client_with_error(httpx.RequestError("Network error"))

        with tempfile.TemporaryDirectory() as tmpdir:
            with patch("httpx.AsyncClient", return_value=mock_client):
                with patch("config.config.UPLOAD_DIR", Path(tmpdir)):
                    with pytest.raises(TranslateEndpointError) as exc_info:
                        await download_from_url("https://example.com/error.xlsx")

                    assert (
                        exc_info.value.code
                        == TranslateEndpointError.URL_DOWNLOAD_FAILED
                    )


# ---------------------------------------------------------------------------
# Task 4: Integration Tests (AC: #7)
# ---------------------------------------------------------------------------


class TestURLIngestionIntegration:
    """Task 4: Tests d'Integration"""

    @pytest.fixture()
    def pro_client(self):
        """Client with Pro user authenticated."""
        from main import app as main_app
        from fastapi.testclient import TestClient

        app.dependency_overrides[get_authenticated_user] = mock_get_pro_user
        client = TestClient(main_app)
        yield client
        app.dependency_overrides.clear()

    @pytest.fixture()
    def free_client(self):
        """Client with Free user authenticated."""
        from main import app as main_app
        from fastapi.testclient import TestClient

        app.dependency_overrides[get_authenticated_user] = mock_get_free_user
        client = TestClient(main_app)
        yield client
        app.dependency_overrides.clear()

    def test_successful_download_pro_user(self, pro_client, monkeypatch):
        """AC2: Pro user can successfully download file from URL"""
        excel_content = create_valid_excel()

        async def mock_download(*args, **kwargs):
            with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as f:
                f.write(excel_content)
                return Path(f.name), "test.xlsx"

        monkeypatch.setattr("routes.translate_routes.download_from_url", mock_download)

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = pro_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": "https://example.com/test.xlsx"},
        )

        assert response.status_code == 202
        body = response.json()
        assert body["data"]["status"] == "processing"
        assert body["data"]["file_name"] == "test.xlsx"

    def test_free_user_rejected(self, free_client, monkeypatch):
        """AC7: Free user receives PRO_FEATURE_REQUIRED (403)"""
        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = free_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": "https://example.com/test.xlsx"},
        )

        assert response.status_code == 403
        body = response.json()
        assert body["error"] == "PRO_FEATURE_REQUIRED"

    def test_url_unreachable_returns_400(self, pro_client, monkeypatch):
        """AC5: URL unreachable returns 400 with URL_UNREACHABLE"""

        async def mock_download(*args, **kwargs):
            raise TranslateEndpointError(
                code=TranslateEndpointError.URL_UNREACHABLE,
                message="URL inaccessible (HTTP 404)",
                details={"status_code": 404},
            )

        monkeypatch.setattr("routes.translate_routes.download_from_url", mock_download)

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = pro_client.post(
            TRANSLATE_URL,
            data={
                "target_lang": "fr",
                "file_url": "https://example.com/notfound.xlsx",
            },
        )

        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "URL_UNREACHABLE"

    def test_file_too_large_returns_413(self, pro_client, monkeypatch):
        """AC4: File > 50MB returns 413 with FILE_TOO_LARGE"""

        async def mock_download(*args, **kwargs):
            raise TranslateEndpointError(
                code=TranslateEndpointError.FILE_TOO_LARGE,
                details={"size_mb": 55, "max_mb": 50},
            )

        monkeypatch.setattr("routes.translate_routes.download_from_url", mock_download)

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = pro_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": "https://example.com/large.xlsx"},
        )

        assert response.status_code == 413
        body = response.json()
        assert body["error"] == "FILE_TOO_LARGE"

    def test_invalid_format_returns_400(self, pro_client, monkeypatch):
        """AC3: Invalid format after download returns 400"""

        async def mock_download(*args, **kwargs):
            raise TranslateEndpointError(
                code=TranslateEndpointError.INVALID_FORMAT,
                details={
                    "detected_extension": ".txt",
                    "accepted_formats": [".xlsx", ".docx", ".pptx"],
                },
            )

        monkeypatch.setattr("routes.translate_routes.download_from_url", mock_download)

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = pro_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": "https://example.com/test.txt"},
        )

        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "INVALID_FORMAT"

    def test_disguised_file_detected(self, pro_client, monkeypatch):
        """AC3: File disguised as .xlsx but with wrong magic bytes detected"""

        async def mock_download(*args, **kwargs):
            raise TranslateEndpointError(
                code=TranslateEndpointError.CORRUPTED_FILE,
                message="Le fichier n'est pas un document Office valide.",
                details={"hint": "Magic bytes validation failed"},
            )

        monkeypatch.setattr("routes.translate_routes.download_from_url", mock_download)

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = pro_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": "https://example.com/fake.xlsx"},
        )

        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "CORRUPTED_FILE"

    def test_download_failed_returns_400(self, pro_client, monkeypatch):
        """AC5: Download failure returns 400 with URL_DOWNLOAD_FAILED"""

        async def mock_download(*args, **kwargs):
            raise TranslateEndpointError(
                code=TranslateEndpointError.URL_DOWNLOAD_FAILED,
                message="Erreur de telechargement: Connection timeout",
                details={"error": "Connection timeout"},
            )

        monkeypatch.setattr("routes.translate_routes.download_from_url", mock_download)

        monkeypatch.setattr(
            "routes.translate_routes.check_usage_limits",
            lambda user: {
                "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"],
            },
        )

        response = pro_client.post(
            TRANSLATE_URL,
            data={"target_lang": "fr", "file_url": "https://example.com/test.xlsx"},
        )

        assert response.status_code == 400
        body = response.json()
        assert body["error"] == "URL_DOWNLOAD_FAILED"
