"""
Tests for app/tasks/upload_to_paperless.py module.

Covers helper functions (normalize_metadata_value, _is_duplicate_error, poll_task_for_document_id,
get_custom_field_id, set_document_custom_fields) and the upload_to_paperless Celery task.
"""

import json
from unittest.mock import Mock, patch

import pytest
import requests

from app.tasks.upload_to_paperless import (
    _get_headers,
    _is_duplicate_error,
    _paperless_api_url,
    get_custom_field_id,
    normalize_metadata_value,
    poll_task_for_document_id,
    set_document_custom_fields,
    upload_to_paperless,
)


@pytest.mark.unit
class TestNormalizeMetadataValue:
    """Tests for normalize_metadata_value function."""

    def test_none_returns_empty(self):
        """Test that None value returns empty string."""
        assert normalize_metadata_value(None) == ""

    def test_empty_string_returns_empty(self):
        """Test that empty string returns empty string."""
        assert normalize_metadata_value("") == ""

    def test_unknown_placeholder_returns_empty(self):
        """Test that 'Unknown' placeholder returns empty string."""
        assert normalize_metadata_value("Unknown") == ""

    def test_normal_string_passes_through(self):
        """Test normal string values pass through."""
        assert normalize_metadata_value("John Doe") == "John Doe"

    def test_integer_converted_to_string(self):
        """Test integer values are converted to string."""
        assert normalize_metadata_value(42) == "42"

    def test_float_converted_to_string(self):
        """Test float values are converted to string."""
        assert normalize_metadata_value(3.14) == "3.14"

    def test_boolean_converted_to_string(self):
        """Test boolean values are converted to string."""
        assert normalize_metadata_value(True) == "True"


@pytest.mark.unit
class TestIsDuplicateError:
    """Tests for _is_duplicate_error function."""

    def test_empty_message(self):
        """Test that empty/None message returns False."""
        assert _is_duplicate_error("") is False
        assert _is_duplicate_error(None) is False

    def test_duplicate_message(self):
        """Test detection of duplicate document message."""
        assert _is_duplicate_error("Not consuming duplicate document") is True

    def test_duplicate_case_insensitive(self):
        """Test case insensitive duplicate detection."""
        assert _is_duplicate_error("DUPLICATE document not consuming") is True

    def test_non_duplicate_message(self):
        """Test that non-duplicate messages return False."""
        assert _is_duplicate_error("Processing completed") is False

    def test_partial_match_not_duplicate(self):
        """Test that message with only 'duplicate' but not 'not consuming' returns False."""
        assert _is_duplicate_error("Found a duplicate") is False


@pytest.mark.unit
class TestGetHeaders:
    """Tests for _get_headers function."""

    def test_returns_auth_header(self):
        """Test that headers include authorization token."""
        with patch("app.tasks.upload_to_paperless.settings") as mock_settings:
            mock_settings.paperless_ngx_api_token = "test-token"
            headers = _get_headers()
            assert headers["Authorization"] == "Token test-token"


@pytest.mark.unit
class TestPaperlessApiUrl:
    """Tests for _paperless_api_url function."""

    def test_constructs_url(self):
        """Test URL construction."""
        with patch("app.tasks.upload_to_paperless.settings") as mock_settings:
            mock_settings.paperless_host = "http://paperless:8000"
            url = _paperless_api_url("/api/documents/")
            assert url == "http://paperless:8000/api/documents/"

    def test_strips_trailing_slash_from_host(self):
        """Test trailing slash is removed from host."""
        with patch("app.tasks.upload_to_paperless.settings") as mock_settings:
            mock_settings.paperless_host = "http://paperless:8000/"
            url = _paperless_api_url("/api/documents/")
            assert url == "http://paperless:8000/api/documents/"

    def test_adds_leading_slash_to_path(self):
        """Test leading slash is added to path if missing."""
        with patch("app.tasks.upload_to_paperless.settings") as mock_settings:
            mock_settings.paperless_host = "http://paperless:8000"
            url = _paperless_api_url("api/documents/")
            assert url == "http://paperless:8000/api/documents/"


@pytest.mark.unit
class TestPollTaskForDocumentId:
    """Tests for poll_task_for_document_id function."""

    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_success_returns_document_id(self, mock_settings, mock_get, mock_sleep):
        """Test successful polling returns document ID."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = [{"status": "SUCCESS", "related_document": "42"}]
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        result = poll_task_for_document_id("test-task-id")
        assert result == 42

    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_success_with_paginated_response(self, mock_settings, mock_get, mock_sleep):
        """Test polling handles paginated API responses."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = {"results": [{"status": "SUCCESS", "related_document": "99"}]}
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        result = poll_task_for_document_id("test-task-id")
        assert result == 99

    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_failure_raises_runtime_error(self, mock_settings, mock_get, mock_sleep):
        """Test that task failure raises RuntimeError."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = [{"status": "FAILURE", "result": "Processing error"}]
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        with pytest.raises(RuntimeError, match="failed"):
            poll_task_for_document_id("test-task-id")

    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_duplicate_returns_none(self, mock_settings, mock_get, mock_sleep):
        """Test that duplicate document failure returns None."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = [{"status": "FAILURE", "result": "Not consuming duplicate document"}]
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        result = poll_task_for_document_id("test-task-id")
        assert result is None

    @patch("app.tasks.upload_to_paperless.POLL_MAX_ATTEMPTS", 2)
    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_timeout_raises_error(self, mock_settings, mock_get, mock_sleep):
        """Test that timeout raises TimeoutError."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        # Return empty results each time
        mock_response = Mock()
        mock_response.json.return_value = []
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        with pytest.raises(TimeoutError):
            poll_task_for_document_id("test-task-id")

    @patch("app.tasks.upload_to_paperless.POLL_MAX_ATTEMPTS", 2)
    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_handles_request_exception(self, mock_settings, mock_get, mock_sleep):
        """Test that request exceptions are handled with retries."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_get.side_effect = requests.exceptions.ConnectionError("Connection refused")

        with pytest.raises(TimeoutError):
            poll_task_for_document_id("test-task-id")

    @patch("app.tasks.upload_to_paperless.time.sleep")
    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_success_without_document_id_raises(self, mock_settings, mock_get, mock_sleep):
        """Test success status without related_document raises RuntimeError."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = [{"status": "SUCCESS", "related_document": None}]
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        with pytest.raises(RuntimeError, match="no doc ID found"):
            poll_task_for_document_id("test-task-id")


@pytest.mark.unit
class TestGetCustomFieldId:
    """Tests for get_custom_field_id function."""

    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_finds_field_by_name(self, mock_settings, mock_get):
        """Test finding a custom field by name."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = {"results": [{"name": "sender", "id": 5}, {"name": "date", "id": 6}]}
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        assert get_custom_field_id("sender") == 5

    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_field_not_found_raises_value_error(self, mock_settings, mock_get):
        """Test that missing field raises ValueError."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = {"results": []}
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        with pytest.raises(ValueError, match="not found"):
            get_custom_field_id("nonexistent")

    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_handles_non_paginated_response(self, mock_settings, mock_get):
        """Test handling of non-paginated API response (list)."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_response = Mock()
        mock_response.json.return_value = [{"name": "sender", "id": 5}]
        mock_response.raise_for_status = Mock()
        mock_get.return_value = mock_response

        assert get_custom_field_id("sender") == 5

    @patch("app.tasks.upload_to_paperless.requests.get")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_request_exception_is_raised(self, mock_settings, mock_get):
        """Test that request exceptions are propagated."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        mock_get.side_effect = requests.exceptions.ConnectionError("Connection refused")

        with pytest.raises(requests.exceptions.ConnectionError):
            get_custom_field_id("sender")


@pytest.mark.unit
class TestSetDocumentCustomFields:
    """Tests for set_document_custom_fields function."""

    @patch("app.tasks.upload_to_paperless.get_custom_field_id")
    @patch("app.tasks.upload_to_paperless.requests.patch")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_sets_custom_fields(self, mock_settings, mock_patch, mock_field_id):
        """Test setting custom fields on a document."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_field_id.return_value = 5
        mock_response = Mock()
        mock_response.raise_for_status = Mock()
        mock_patch.return_value = mock_response

        set_document_custom_fields(42, {"sender": "John Doe"}, "task-123")

        mock_patch.assert_called_once()

    def test_empty_fields_returns_immediately(self):
        """Test that empty custom fields dict returns without API calls."""
        with patch("app.tasks.upload_to_paperless.requests.patch") as mock_patch:
            set_document_custom_fields(42, {}, "task-123")
            mock_patch.assert_not_called()

    @patch("app.tasks.upload_to_paperless.get_custom_field_id")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_skips_unknown_values(self, mock_settings, mock_field_id):
        """Test that 'Unknown' values are skipped."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        with patch("app.tasks.upload_to_paperless.requests.patch") as mock_patch:
            set_document_custom_fields(42, {"sender": "Unknown"}, "task-123")
            mock_patch.assert_not_called()

    @patch("app.tasks.upload_to_paperless.get_custom_field_id")
    @patch("app.tasks.upload_to_paperless.requests.patch")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_skips_field_not_found(self, mock_settings, mock_patch, mock_field_id):
        """Test that fields not found in Paperless are skipped."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_field_id.side_effect = ValueError("Custom field 'foo' not found")

        set_document_custom_fields(42, {"foo": "bar"}, "task-123")

        mock_patch.assert_not_called()

    @patch("app.tasks.upload_to_paperless.get_custom_field_id")
    @patch("app.tasks.upload_to_paperless.requests.patch")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_handles_patch_failure(self, mock_settings, mock_patch, mock_field_id):
        """Test that PATCH failure is logged but does not raise."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_field_id.return_value = 5

        mock_exc = requests.exceptions.HTTPError("500 Server Error")
        mock_exc.response = Mock()
        mock_exc.response.text = "Internal server error"
        mock_patch.side_effect = mock_exc

        # Should not raise
        set_document_custom_fields(42, {"sender": "John"}, "task-123")


@pytest.mark.unit
class TestUploadToPaperless:
    """Tests for upload_to_paperless Celery task."""

    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_file_not_found(self, mock_log):
        """Test that missing file raises FileNotFoundError."""
        with pytest.raises(FileNotFoundError):
            upload_to_paperless.__wrapped__("/nonexistent/file.pdf", file_id=1)

    @patch("app.tasks.upload_to_paperless.set_document_custom_fields")
    @patch("app.tasks.upload_to_paperless.poll_task_for_document_id")
    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_successful_upload(self, mock_log, mock_settings, mock_post, mock_poll, mock_set_fields, tmp_path):
        """Test successful upload to Paperless."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_settings.paperless_custom_fields_mapping = None
        mock_settings.paperless_custom_field_absender = None

        # Create test file
        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        mock_response = Mock()
        mock_response.text = '"task-uuid-123"'
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        mock_poll.return_value = 42

        result = upload_to_paperless.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()

        assert result["status"] == "Completed"
        assert result["paperless_document_id"] == 42

    @patch("app.tasks.upload_to_paperless.poll_task_for_document_id")
    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_duplicate_document(self, mock_log, mock_settings, mock_post, mock_poll, tmp_path):
        """Test handling of duplicate document detection."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_settings.paperless_custom_fields_mapping = None
        mock_settings.paperless_custom_field_absender = None

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        mock_response = Mock()
        mock_response.text = '"task-uuid-123"'
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        mock_poll.return_value = None  # Duplicate detected

        result = upload_to_paperless.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()

        assert result["status"] == "Duplicate"
        assert result["paperless_document_id"] is None

    @patch("app.tasks.upload_to_paperless.log_task_progress")
    @patch("app.tasks.upload_to_paperless.settings")
    def test_missing_config_raises_value_error(self, mock_settings, mock_log, tmp_path):
        """Test that missing Paperless config raises ValueError."""
        mock_settings.paperless_host = ""
        mock_settings.paperless_ngx_api_token = ""

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        with pytest.raises(ValueError, match="not fully configured"):
            upload_to_paperless.__wrapped__(str(test_file), file_id=1)

    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_upload_request_failure(self, mock_log, mock_settings, mock_post, tmp_path):
        """Test that failed HTTP request raises."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        mock_exc = requests.exceptions.ConnectionError("Connection refused")
        mock_exc.response = None
        mock_post.side_effect = mock_exc

        with pytest.raises(requests.exceptions.ConnectionError):
            upload_to_paperless.__wrapped__(str(test_file), file_id=1)

    @patch("app.tasks.upload_to_paperless.set_document_custom_fields")
    @patch("app.tasks.upload_to_paperless.poll_task_for_document_id")
    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_loads_metadata_from_json(self, mock_log, mock_settings, mock_post, mock_poll, mock_set_fields, tmp_path):
        """Test that metadata is loaded from accompanying JSON file."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_settings.paperless_custom_fields_mapping = json.dumps({"absender": "Sender"})
        mock_settings.paperless_custom_field_absender = None

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        # Create metadata JSON
        json_file = tmp_path / "test.json"
        json_file.write_text(json.dumps({"absender": "Test Sender", "date": "2024-01-01"}))

        mock_response = Mock()
        mock_response.text = '"task-uuid-123"'
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        mock_poll.return_value = 42

        result = upload_to_paperless.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()

        assert result["status"] == "Completed"
        mock_set_fields.assert_called_once()
        # Verify the custom fields include the mapped metadata
        call_args = mock_set_fields.call_args
        assert "Sender" in call_args[0][1]

    @patch("app.tasks.upload_to_paperless.set_document_custom_fields")
    @patch("app.tasks.upload_to_paperless.poll_task_for_document_id")
    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_legacy_absender_field(self, mock_log, mock_settings, mock_post, mock_poll, mock_set_fields, tmp_path):
        """Test legacy absender field configuration fallback."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_settings.paperless_custom_fields_mapping = None
        mock_settings.paperless_custom_field_absender = "Absender"

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        json_file = tmp_path / "test.json"
        json_file.write_text(json.dumps({"absender": "Legacy Sender"}))

        mock_response = Mock()
        mock_response.text = '"task-uuid-123"'
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        mock_poll.return_value = 42

        result = upload_to_paperless.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()

        assert result["status"] == "Completed"
        mock_set_fields.assert_called_once()
        call_args = mock_set_fields.call_args
        assert "Absender" in call_args[0][1]

    @patch("app.tasks.upload_to_paperless.set_document_custom_fields")
    @patch("app.tasks.upload_to_paperless.poll_task_for_document_id")
    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_invalid_json_mapping_handled(
        self, mock_log, mock_settings, mock_post, mock_poll, mock_set_fields, tmp_path
    ):
        """Test that invalid JSON mapping does not crash the task."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_settings.paperless_custom_fields_mapping = "not-valid-json"
        mock_settings.paperless_custom_field_absender = None

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        mock_response = Mock()
        mock_response.text = '"task-uuid-123"'
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        mock_poll.return_value = 42

        result = upload_to_paperless.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()

        assert result["status"] == "Completed"

    @patch("app.tasks.upload_to_paperless.set_document_custom_fields")
    @patch("app.tasks.upload_to_paperless.poll_task_for_document_id")
    @patch("app.tasks.upload_to_paperless.requests.post")
    @patch("app.tasks.upload_to_paperless.settings")
    @patch("app.tasks.upload_to_paperless.log_task_progress")
    def test_custom_fields_failure_does_not_fail_upload(
        self, mock_log, mock_settings, mock_post, mock_poll, mock_set_fields, tmp_path
    ):
        """Test that custom field errors don't fail the entire upload."""
        mock_settings.paperless_host = "http://paperless:8000"
        mock_settings.paperless_ngx_api_token = "test-token"
        mock_settings.http_request_timeout = 30
        mock_settings.paperless_custom_fields_mapping = json.dumps({"absender": "Sender"})
        mock_settings.paperless_custom_field_absender = None

        test_file = tmp_path / "test.pdf"
        test_file.write_bytes(b"%PDF-1.4 test content")

        json_file = tmp_path / "test.json"
        json_file.write_text(json.dumps({"absender": "Test"}))

        mock_response = Mock()
        mock_response.text = '"task-uuid-123"'
        mock_response.raise_for_status = Mock()
        mock_post.return_value = mock_response

        mock_poll.return_value = 42
        mock_set_fields.side_effect = Exception("Custom fields error")

        result = upload_to_paperless.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()

        # Upload should still succeed even though custom fields failed
        assert result["status"] == "Completed"
