"""
Tests for storage backend integration in web application.

TDD Phase 1: RED - Write tests first, then implement to pass.
"""

import os
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest


class TestStorageBackendInitialization:
    """Tests for storage backend initialization in web config."""

    def test_get_storage_backend_returns_backend(self, tmp_path: Path) -> None:
        """Test that get_storage_backend returns a StorageBackend instance."""
        from shared.storage.base import StorageBackend

        from backend.web.config import get_storage_backend

        env = {
            "STORAGE_BACKEND": "local",
            "STORAGE_BASE_PATH": str(tmp_path / "storage"),
        }

        with patch.dict(os.environ, env, clear=False):
            backend = get_storage_backend()

        assert isinstance(backend, StorageBackend)

    def test_get_storage_backend_uses_config_file_if_exists(
        self, tmp_path: Path
    ) -> None:
        """Test that storage config file is used when present."""
        from shared.storage.local import LocalStorageBackend

        from backend.web.config import get_storage_backend

        config_file = tmp_path / "storage.yaml"
        storage_path = tmp_path / "storage"
        config_file.write_text(f"""
backend: local

local:
  base_path: {storage_path}
""")

        backend = get_storage_backend(config_path=config_file)

        assert isinstance(backend, LocalStorageBackend)

    def test_get_storage_backend_falls_back_to_env(self, tmp_path: Path) -> None:
        """Test fallback to environment variables when no config file."""
        from shared.storage.local import LocalStorageBackend

        from backend.web.config import get_storage_backend

        env = {
            "STORAGE_BACKEND": "local",
            "STORAGE_BASE_PATH": str(tmp_path / "storage"),
        }

        with patch.dict(os.environ, env, clear=False):
            backend = get_storage_backend(config_path=None)

        assert isinstance(backend, LocalStorageBackend)

    def test_app_config_has_storage_backend(self, tmp_path: Path) -> None:
        """Test that AppConfig can be created with storage backend."""
        from shared.storage.base import StorageBackend

        from backend.web.config import AppConfig, create_app_config

        env = {
            "STORAGE_BACKEND": "local",
            "STORAGE_BASE_PATH": str(tmp_path / "storage"),
        }

        with patch.dict(os.environ, env, clear=False):
            config = create_app_config()

        assert hasattr(config, "storage_backend")
        assert isinstance(config.storage_backend, StorageBackend)


class TestStorageBackendInDocumentUpload:
    """Tests for storage backend usage in document upload."""

    def test_upload_document_uses_storage_backend(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that document upload uses storage backend."""
        from unittest.mock import AsyncMock

        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        # Create a mock upload file
        pdf_content = b"%PDF-1.4 test content"

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        # Upload should use storage backend
        result = service.upload_document(
            content=pdf_content,
            filename="test.pdf",
            dataset_id="dataset-1",
        )

        assert result is not None
        # Verify file was stored via storage backend
        assert backend.exists(f"documents/{result.id}.pdf")

    def test_upload_document_stores_logical_path(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that document stores logical path, not absolute path."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        pdf_content = b"%PDF-1.4 test content"

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        result = service.upload_document(
            content=pdf_content,
            filename="test.pdf",
            dataset_id="dataset-1",
        )

        # Path should be logical (relative), not absolute
        assert not result.file_path.startswith("/")
        assert not result.file_path.startswith("C:")
        assert result.file_path.startswith("documents/")


class TestStorageBackendInDocumentDownload:
    """Tests for storage backend usage in document download/serving."""

    def test_get_document_url_returns_presigned_url(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that document URL uses presigned URL from storage backend."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        # Create a test file
        doc_path = "documents/test-doc.pdf"
        backend.upload_bytes(b"%PDF-1.4 test", doc_path)

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        url = service.get_document_url(doc_path)

        # Should return a URL (file:// for local, https:// for cloud)
        assert url is not None
        assert "test-doc.pdf" in url

    def test_download_document_uses_storage_backend(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that document download uses storage backend."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        # Create a test file
        doc_path = "documents/test-doc.pdf"
        original_content = b"%PDF-1.4 test content"
        backend.upload_bytes(original_content, doc_path)

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        content = service.download_document(doc_path)

        assert content == original_content


class TestStorageBackendInImageServing:
    """Tests for storage backend usage in image serving."""

    def test_get_page_image_url_returns_presigned_url(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that page image URL uses presigned URL."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        # Create a test image
        image_path = "images/doc-123/page_1.png"
        backend.upload_bytes(b"fake png content", image_path)

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        url = service.get_page_image_url("doc-123", 1)

        assert url is not None
        assert "page_1.png" in url

    def test_save_page_image_uses_storage_backend(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that page image saving uses storage backend."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        image_content = b"fake png content"
        service.save_page_image("doc-123", 1, image_content)

        # Verify image was stored
        assert backend.exists("images/doc-123/page_1.png")


class TestStorageBackendInDocumentDeletion:
    """Tests for storage backend usage in document deletion."""

    def test_delete_document_removes_from_storage(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that document deletion removes file from storage."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        # Create test files
        doc_path = "documents/test-doc.pdf"
        backend.upload_bytes(b"%PDF-1.4 test", doc_path)

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        service.delete_document_files(doc_path)

        assert not backend.exists(doc_path)

    def test_delete_document_removes_images(
        self, tmp_path: Path, mock_admin_db: MagicMock
    ) -> None:
        """Test that document deletion removes associated images."""
        from shared.storage.local import LocalStorageBackend

        storage_path = tmp_path / "storage"
        storage_path.mkdir(parents=True, exist_ok=True)
        backend = LocalStorageBackend(str(storage_path))

        # Create test files
        doc_id = "test-doc-123"
        backend.upload_bytes(b"img1", f"images/{doc_id}/page_1.png")
        backend.upload_bytes(b"img2", f"images/{doc_id}/page_2.png")

        from backend.web.services.document_service import DocumentService

        service = DocumentService(admin_db=mock_admin_db, storage_backend=backend)

        service.delete_document_images(doc_id)

        assert not backend.exists(f"images/{doc_id}/page_1.png")
        assert not backend.exists(f"images/{doc_id}/page_2.png")


@pytest.fixture
def mock_admin_db() -> MagicMock:
    """Create a mock AdminDB for testing."""
    mock = MagicMock()
    mock.get_document.return_value = None
    mock.create_document.return_value = MagicMock(
        id="test-doc-id",
        file_path="documents/test-doc-id.pdf",
    )
    return mock
