"""
Tests for research_library/deletion/routes/delete_routes.py

Tests cover:
- DELETE /document/<id> - single document deletion
- DELETE /document/<id>/blob - blob only deletion
- GET /document/<id>/preview - document deletion preview
- DELETE /collection/<id>/document/<id> - remove from collection
- DELETE /collections/<id> - collection deletion
- DELETE /collections/<id>/index - collection index deletion
- GET /collections/<id>/preview - collection deletion preview
- DELETE /documents/bulk - bulk document deletion
- DELETE /documents/blobs - bulk blob deletion
- DELETE /collection/<id>/documents/bulk - bulk removal from collection
- POST /documents/preview - bulk deletion preview
"""

import pytest
from unittest.mock import MagicMock, patch
from flask import Flask


class TestDeleteBlueprintImport:
    """Tests for blueprint import and registration."""

    def test_blueprint_exists(self):
        """Test that delete blueprint exists."""
        from local_deep_research.research_library.deletion.routes.delete_routes import (
            delete_bp,
        )

        assert delete_bp is not None
        assert delete_bp.name == "delete"
        assert delete_bp.url_prefix == "/library/api"


class TestDeleteDocumentEndpoint:
    """Tests for DELETE /document/<id> endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_delete_document_service_called_correctly(self, mock_service_class):
        """Test that DocumentDeletionService is called with correct arguments."""
        mock_service = MagicMock()
        mock_service.delete_document.return_value = {"deleted": True}
        mock_service_class.return_value = mock_service

        # Just verify the service mock setup works
        service = mock_service_class("testuser")
        result = service.delete_document("doc123")

        mock_service.delete_document.assert_called_once_with("doc123")
        assert result["deleted"] is True

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_delete_document_not_found_returns_false(self, mock_service_class):
        """Test document deletion when document not found."""
        mock_service = MagicMock()
        mock_service.delete_document.return_value = {
            "deleted": False,
            "error": "Document not found",
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_document("nonexistent")

        assert result["deleted"] is False
        assert "error" in result


class TestDeleteDocumentBlobEndpoint:
    """Tests for DELETE /document/<id>/blob endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_delete_blob_service_called_correctly(self, mock_service_class):
        """Test that delete_blob_only is called correctly."""
        mock_service = MagicMock()
        mock_service.delete_blob_only.return_value = {
            "deleted": True,
            "bytes_freed": 2048,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_blob_only("doc123")

        mock_service.delete_blob_only.assert_called_once_with("doc123")
        assert result["deleted"] is True
        assert result["bytes_freed"] == 2048

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_delete_blob_not_found(self, mock_service_class):
        """Test blob deletion when document not found."""
        mock_service = MagicMock()
        mock_service.delete_blob_only.return_value = {
            "deleted": False,
            "error": "Document not found",
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_blob_only("nonexistent")

        assert result["deleted"] is False


class TestDocumentDeletionPreviewEndpoint:
    """Tests for GET /document/<id>/preview endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_preview_service_called_correctly(self, mock_service_class):
        """Test that get_deletion_preview is called correctly."""
        mock_service = MagicMock()
        mock_service.get_deletion_preview.return_value = {
            "found": True,
            "title": "Test Document",
            "chunks_count": 15,
            "blob_size": 4096,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.get_deletion_preview("doc123")

        mock_service.get_deletion_preview.assert_called_once_with("doc123")
        assert result["found"] is True
        assert result["title"] == "Test Document"

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_preview_not_found(self, mock_service_class):
        """Test preview for nonexistent document."""
        mock_service = MagicMock()
        mock_service.get_deletion_preview.return_value = {"found": False}
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.get_deletion_preview("nonexistent")

        assert result["found"] is False


class TestRemoveDocumentFromCollectionEndpoint:
    """Tests for DELETE /collection/<coll_id>/document/<doc_id> endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_remove_from_collection_success(self, mock_service_class):
        """Test successful removal from collection."""
        mock_service = MagicMock()
        mock_service.remove_from_collection.return_value = {
            "unlinked": True,
            "document_deleted": False,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.remove_from_collection("doc123", "coll123")

        mock_service.remove_from_collection.assert_called_once_with(
            "doc123", "coll123"
        )
        assert result["unlinked"] is True
        assert result["document_deleted"] is False

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_remove_orphan_document_deleted(self, mock_service_class):
        """Test that orphaned document is deleted."""
        mock_service = MagicMock()
        mock_service.remove_from_collection.return_value = {
            "unlinked": True,
            "document_deleted": True,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.remove_from_collection("doc123", "coll123")

        assert result["unlinked"] is True
        assert result["document_deleted"] is True


class TestDeleteCollectionEndpoint:
    """Tests for DELETE /collections/<id> endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.CollectionDeletionService"
    )
    def test_delete_collection_success(self, mock_service_class):
        """Test successful collection deletion."""
        mock_service = MagicMock()
        mock_service.delete_collection.return_value = {
            "deleted": True,
            "documents_unlinked": 5,
            "chunks_deleted": 150,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_collection("coll123")

        mock_service.delete_collection.assert_called_once_with("coll123")
        assert result["deleted"] is True
        assert result["documents_unlinked"] == 5

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.CollectionDeletionService"
    )
    def test_delete_collection_not_found(self, mock_service_class):
        """Test collection deletion when not found."""
        mock_service = MagicMock()
        mock_service.delete_collection.return_value = {
            "deleted": False,
            "error": "Collection not found",
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_collection("nonexistent")

        assert result["deleted"] is False


class TestDeleteCollectionIndexEndpoint:
    """Tests for DELETE /collections/<id>/index endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.CollectionDeletionService"
    )
    def test_delete_index_success(self, mock_service_class):
        """Test successful index deletion."""
        mock_service = MagicMock()
        mock_service.delete_collection_index_only.return_value = {
            "deleted": True,
            "chunks_deleted": 200,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_collection_index_only("coll123")

        mock_service.delete_collection_index_only.assert_called_once_with(
            "coll123"
        )
        assert result["deleted"] is True
        assert result["chunks_deleted"] == 200


class TestCollectionDeletionPreviewEndpoint:
    """Tests for GET /collections/<id>/preview endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.CollectionDeletionService"
    )
    def test_collection_preview_success(self, mock_service_class):
        """Test successful collection preview."""
        mock_service = MagicMock()
        mock_service.get_deletion_preview.return_value = {
            "found": True,
            "name": "Test Collection",
            "document_count": 10,
            "chunk_count": 500,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.get_deletion_preview("coll123")

        assert result["found"] is True
        assert result["name"] == "Test Collection"


class TestBulkDeleteDocumentsEndpoint:
    """Tests for DELETE /documents/bulk endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_delete_success(self, mock_service_class):
        """Test successful bulk deletion."""
        mock_service = MagicMock()
        mock_service.delete_documents.return_value = {
            "deleted": 3,
            "failed": 0,
            "total_chunks_deleted": 50,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_documents(["doc1", "doc2", "doc3"])

        mock_service.delete_documents.assert_called_once_with(
            ["doc1", "doc2", "doc3"]
        )
        assert result["deleted"] == 3
        assert result["failed"] == 0

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_delete_partial_failure(self, mock_service_class):
        """Test bulk deletion with partial failures."""
        mock_service = MagicMock()
        mock_service.delete_documents.return_value = {
            "deleted": 2,
            "failed": 1,
            "errors": [{"id": "doc3", "error": "Not found"}],
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_documents(["doc1", "doc2", "doc3"])

        assert result["deleted"] == 2
        assert result["failed"] == 1


class TestBulkDeleteBlobsEndpoint:
    """Tests for DELETE /documents/blobs endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_delete_blobs_success(self, mock_service_class):
        """Test successful bulk blob deletion."""
        mock_service = MagicMock()
        mock_service.delete_blobs.return_value = {
            "deleted": 2,
            "failed": 0,
            "bytes_freed": 8192,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.delete_blobs(["doc1", "doc2"])

        mock_service.delete_blobs.assert_called_once_with(["doc1", "doc2"])
        assert result["deleted"] == 2
        assert result["bytes_freed"] == 8192


class TestBulkRemoveFromCollectionEndpoint:
    """Tests for DELETE /collection/<id>/documents/bulk endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_remove_success(self, mock_service_class):
        """Test successful bulk removal from collection."""
        mock_service = MagicMock()
        mock_service.remove_documents_from_collection.return_value = {
            "unlinked": 3,
            "documents_deleted": 1,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.remove_documents_from_collection(
            ["doc1", "doc2", "doc3"], "coll123"
        )

        mock_service.remove_documents_from_collection.assert_called_once_with(
            ["doc1", "doc2", "doc3"], "coll123"
        )
        assert result["unlinked"] == 3
        assert result["documents_deleted"] == 1


class TestBulkDeletionPreviewEndpoint:
    """Tests for POST /documents/preview endpoint."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_preview_success(self, mock_service_class):
        """Test successful bulk preview."""
        mock_service = MagicMock()
        mock_service.get_bulk_preview.return_value = {
            "document_count": 3,
            "total_chunks": 75,
            "total_blob_size": 12288,
        }
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")
        result = service.get_bulk_preview(["doc1", "doc2", "doc3"], "delete")

        mock_service.get_bulk_preview.assert_called_once_with(
            ["doc1", "doc2", "doc3"], "delete"
        )
        assert result["document_count"] == 3
        assert result["total_chunks"] == 75

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_preview_delete_blobs_operation(self, mock_service_class):
        """Test bulk preview with delete_blobs operation."""
        mock_service = MagicMock()
        mock_service.get_bulk_preview.return_value = {
            "document_count": 2,
            "total_blob_size": 4096,
        }
        mock_service_class.return_value = mock_service

        _service = mock_service_class("testuser")
        _result = _service.get_bulk_preview(["doc1", "doc2"], "delete_blobs")

        mock_service.get_bulk_preview.assert_called_once_with(
            ["doc1", "doc2"], "delete_blobs"
        )


class TestRequestValidation:
    """Tests for request validation logic."""

    def test_document_ids_must_be_list(self):
        """Test that document_ids must be a list."""
        # Simulate the validation logic from the route
        data = {"document_ids": "not-a-list"}
        is_valid = (
            isinstance(data.get("document_ids"), list) and data["document_ids"]
        )
        assert is_valid is False

    def test_document_ids_cannot_be_empty(self):
        """Test that document_ids cannot be empty."""
        data = {"document_ids": []}
        is_valid = (
            isinstance(data.get("document_ids"), list)
            and len(data["document_ids"]) > 0
        )
        assert is_valid is False

    def test_document_ids_required(self):
        """Test that document_ids field is required."""
        data = {}
        has_document_ids = "document_ids" in data
        assert has_document_ids is False

    def test_valid_document_ids(self):
        """Test valid document_ids format."""
        data = {"document_ids": ["doc1", "doc2", "doc3"]}
        is_valid = (
            isinstance(data.get("document_ids"), list)
            and len(data["document_ids"]) > 0
        )
        assert is_valid is True


class TestErrorHandling:
    """Tests for error handling patterns."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_service_exception_handling(self, mock_service_class):
        """Test that service exceptions are handled."""
        mock_service = MagicMock()
        mock_service.delete_document.side_effect = Exception("Database error")
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")

        with pytest.raises(Exception) as exc_info:
            service.delete_document("doc123")

        assert "Database error" in str(exc_info.value)

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.CollectionDeletionService"
    )
    def test_collection_service_exception(self, mock_service_class):
        """Test collection service exception handling."""
        mock_service = MagicMock()
        mock_service.delete_collection.side_effect = ValueError(
            "Invalid collection"
        )
        mock_service_class.return_value = mock_service

        service = mock_service_class("testuser")

        with pytest.raises(ValueError) as exc_info:
            service.delete_collection("coll123")

        assert "Invalid collection" in str(exc_info.value)


class TestResponseFormats:
    """Tests for response format consistency."""

    def test_delete_success_response_format(self):
        """Test successful deletion response format."""
        response = {
            "deleted": True,
            "document_id": "doc123",
            "chunks_deleted": 10,
        }
        assert "deleted" in response
        assert response["deleted"] is True

    def test_delete_failure_response_format(self):
        """Test failed deletion response format."""
        response = {
            "deleted": False,
            "error": "Document not found",
        }
        assert "deleted" in response
        assert response["deleted"] is False
        assert "error" in response

    def test_preview_response_format(self):
        """Test preview response format."""
        response = {
            "found": True,
            "title": "Test Document",
            "chunks_count": 15,
            "blob_size": 4096,
        }
        assert "found" in response
        assert response["found"] is True

    def test_bulk_response_format(self):
        """Test bulk operation response format."""
        response = {
            "deleted": 3,
            "failed": 0,
            "total_chunks_deleted": 50,
        }
        assert "deleted" in response
        assert "failed" in response


class TestServiceIntegration:
    """Tests for service integration patterns."""

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.DocumentDeletionService"
    )
    def test_service_created_with_username(self, mock_service_class):
        """Test that services are created with username."""
        mock_service = MagicMock()
        mock_service_class.return_value = mock_service

        # Simulate how the route creates the service
        username = "testuser"
        _service = mock_service_class(username)

        mock_service_class.assert_called_once_with(username)

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.CollectionDeletionService"
    )
    def test_collection_service_created_with_username(self, mock_service_class):
        """Test that collection services are created with username."""
        mock_service = MagicMock()
        mock_service_class.return_value = mock_service

        username = "testuser"
        _service = mock_service_class(username)

        mock_service_class.assert_called_once_with(username)

    @patch(
        "local_deep_research.research_library.deletion.routes.delete_routes.BulkDeletionService"
    )
    def test_bulk_service_created_with_username(self, mock_service_class):
        """Test that bulk services are created with username."""
        mock_service = MagicMock()
        mock_service_class.return_value = mock_service

        username = "testuser"
        _service = mock_service_class(username)

        mock_service_class.assert_called_once_with(username)


class TestEdgeCases:
    """Edge case tests."""

    def test_uuid_format_document_id(self):
        """Test UUID format document ID is valid."""
        import uuid

        doc_id = str(uuid.uuid4())
        assert len(doc_id) == 36  # Standard UUID format

    def test_empty_string_document_id(self):
        """Test that empty string ID is invalid."""
        doc_id = ""
        is_valid = bool(doc_id)
        assert is_valid is False

    def test_whitespace_only_document_id(self):
        """Test that whitespace-only ID is invalid."""
        doc_id = "   "
        is_valid = bool(doc_id.strip())
        assert is_valid is False

    def test_very_long_document_id(self):
        """Test handling of very long document ID."""
        doc_id = "a" * 1000
        # Should still be valid string
        assert isinstance(doc_id, str)
        assert len(doc_id) == 1000

    def test_special_characters_in_id(self):
        """Test special characters in document ID."""
        special_ids = [
            "doc-123",
            "doc_123",
            "doc.123",
            "doc:123",
        ]
        for doc_id in special_ids:
            assert isinstance(doc_id, str)

    def test_unicode_document_id(self):
        """Test unicode characters in document ID."""
        doc_id = "文档123"
        assert isinstance(doc_id, str)
        assert len(doc_id) == 5


class TestHandleApiErrorIntegration:
    """Tests for handle_api_error helper."""

    def test_handle_api_error_imported(self):
        """Test that handle_api_error is available."""
        from local_deep_research.research_library.utils import handle_api_error

        assert callable(handle_api_error)

    def test_handle_api_error_returns_tuple(self):
        """Test that handle_api_error returns proper format."""
        from flask import Flask
        from local_deep_research.research_library.utils import handle_api_error

        app = Flask(__name__)
        with app.app_context():
            result = handle_api_error("test operation", Exception("Test error"))

            # Should return a tuple (response, status_code)
            assert isinstance(result, tuple)
            assert len(result) == 2
            assert result[1] == 500  # Default status code


class TestDeleteRoutesModuleImport:
    """Tests for module imports."""

    def test_all_services_importable(self):
        """Test that all deletion services are importable."""
        from local_deep_research.research_library.deletion.services.document_deletion import (
            DocumentDeletionService,
        )
        from local_deep_research.research_library.deletion.services.collection_deletion import (
            CollectionDeletionService,
        )
        from local_deep_research.research_library.deletion.services.bulk_deletion import (
            BulkDeletionService,
        )

        assert DocumentDeletionService is not None
        assert CollectionDeletionService is not None
        assert BulkDeletionService is not None

    def test_blueprint_routes_registered(self):
        """Test that all routes are registered on the blueprint."""
        from local_deep_research.research_library.deletion.routes.delete_routes import (
            delete_bp,
        )

        # Get all registered rules
        app = Flask(__name__)
        app.register_blueprint(delete_bp)

        rules = [rule.rule for rule in app.url_map.iter_rules()]

        expected_routes = [
            "/library/api/document/<string:document_id>",
            "/library/api/document/<string:document_id>/blob",
            "/library/api/document/<string:document_id>/preview",
            "/library/api/collection/<string:collection_id>/document/<string:document_id>",
            "/library/api/collections/<string:collection_id>",
            "/library/api/collections/<string:collection_id>/index",
            "/library/api/collections/<string:collection_id>/preview",
            "/library/api/documents/bulk",
            "/library/api/documents/blobs",
            "/library/api/collection/<string:collection_id>/documents/bulk",
            "/library/api/documents/preview",
        ]

        for expected in expected_routes:
            assert expected in rules, f"Expected route {expected} not found"
