import pytest
import hashlib
from fastapi.testclient import TestClient
from unittest.mock import patch, AsyncMock, MagicMock
from main import app
from routes.translate_routes import get_authenticated_user

@pytest.fixture()
def client(monkeypatch):
    """TestClient with rate limiting and quota reservation bypassed for metadata tests."""
    from middleware.rate_limiting import RateLimitMiddleware

    async def _dispatch(self, request, call_next):
        return await call_next(request)

    monkeypatch.setattr(RateLimitMiddleware, "dispatch", _dispatch)
    monkeypatch.setattr("routes.translate_routes.reserve_translation_quota", lambda user_id: True)
    from main import app

    return TestClient(app)


class MockUser:
    def __init__(self, user_id="user_123"):
        self.id = user_id
        self.plan = "free"
        self.docs_translated_this_month = 0
        self.pages_translated_this_month = 0
        self.extra_credits = 0


async def mock_auth():
    return MockUser()


@pytest.mark.asyncio
async def test_translate_endpoint_triggers_tracking(client):
    app.dependency_overrides[get_authenticated_user] = mock_auth

    with patch(
        "routes.translate_routes.storage_tracker.track_file", new_callable=AsyncMock
    ) as mock_track:
        # The upload write is mocked below, so no real archive exists on
        # disk: neutralize the zip safety check for this test.
        with patch(
            "routes.translate_routes.file_validator.validate_async"
        ) as mock_val, patch("routes.translate_routes.validate_zip_safety"):
            mock_val.return_value.is_valid = True
            mock_val.return_value.data = {"extension": ".docx", "size_bytes": 500}

            file_content = b"PK\x03\x04fake_office_content_for_testing"
            with patch(
                "routes.translate_routes.file_handler_util.save_upload_file",
                new_callable=AsyncMock,
            ) as mock_save:
                with patch(
                    "routes.translate_routes.file_handler_util.calculate_sha256"
                ) as mock_hash:
                    with patch(
                        "routes.translate_routes.file_handler_util.cleanup_file"
                    ) as mock_cleanup:
                        mock_save.return_value = None
                        expected_hash = hashlib.sha256(file_content).hexdigest()
                        mock_hash.return_value = expected_hash

                        files = {
                            "file": (
                                "test.docx",
                                file_content,
                                "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                            )
                        }
                        response = client.post(
                            "/api/v1/translate", data={"target_lang": "fr"}, files=files
                        )

                        assert response.status_code == 202
                        job_id = response.json()["data"]["id"]

                        mock_track.assert_called_once()
                        args, kwargs = mock_track.call_args
                        assert kwargs["job_id"] == job_id
                        assert kwargs["metadata"]["original_filename"] == "test.docx"
                        assert kwargs["metadata"]["file_hash"] == expected_hash
                        assert kwargs["metadata"]["user_id"] == "user_123"
                        assert "timestamp" in kwargs["metadata"]

    app.dependency_overrides.clear()


@pytest.mark.asyncio
async def test_translate_endpoint_handles_hash_failure(client):
    app.dependency_overrides[get_authenticated_user] = mock_auth

    # No real archive exists on disk (save is mocked): skip the zip check.
    with patch(
        "routes.translate_routes.file_validator.validate_async"
    ) as mock_val, patch("routes.translate_routes.validate_zip_safety"):
        mock_val.return_value.is_valid = True
        mock_val.return_value.data = {"extension": ".docx", "size_bytes": 500}

        file_content = b"PK\x03\x04fake_office_content"
        with patch(
            "routes.translate_routes.file_handler_util.save_upload_file",
            new_callable=AsyncMock,
        ):
            with patch(
                "routes.translate_routes.file_handler_util.calculate_sha256",
                return_value=None,
            ):
                with patch(
                    "routes.translate_routes.file_handler_util.cleanup_file"
                ) as mock_cleanup:
                    files = {
                        "file": (
                            "test.docx",
                            file_content,
                            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                        )
                    }
                    response = client.post(
                        "/api/v1/translate", data={"target_lang": "fr"}, files=files
                    )

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

    app.dependency_overrides.clear()
