"""Tests for should_produce_archive()."""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING
from unittest.mock import MagicMock

import pytest

from documents.consumer import should_produce_archive

if TYPE_CHECKING:
    from pytest_mock import MockerFixture


def _parser_instance(
    *,
    can_produce: bool = True,
    requires_rendition: bool = False,
) -> MagicMock:
    """Return a mock parser instance with the given capability flags."""
    instance = MagicMock()
    instance.can_produce_archive = can_produce
    instance.requires_pdf_rendition = requires_rendition
    return instance


@pytest.fixture()
def null_app_config(mocker) -> MagicMock:
    """Mock ApplicationConfiguration with all fields None → falls back to Django settings."""
    return mocker.MagicMock(
        output_type=None,
        pages=None,
        language=None,
        mode=None,
        archive_file_generation=None,
        image_dpi=None,
        unpaper_clean=None,
        deskew=None,
        rotate_pages=None,
        rotate_pages_threshold=None,
        max_image_pixels=None,
        color_conversion_strategy=None,
        user_args=None,
    )


@pytest.fixture(autouse=True)
def patch_app_config(mocker, null_app_config):
    """Patch BaseConfig._get_config_instance for all tests in this module."""
    mocker.patch(
        "paperless.config.BaseConfig._get_config_instance",
        return_value=null_app_config,
    )


class TestShouldProduceArchive:
    @pytest.mark.parametrize(
        ("generation", "can_produce", "requires_rendition", "mime", "expected"),
        [
            pytest.param(
                "never",
                True,
                False,
                "application/pdf",
                False,
                id="never-returns-false",
            ),
            pytest.param(
                "always",
                True,
                False,
                "application/pdf",
                True,
                id="always-returns-true",
            ),
            pytest.param(
                "never",
                True,
                True,
                "application/pdf",
                True,
                id="requires-rendition-overrides-never",
            ),
            pytest.param(
                "always",
                False,
                False,
                "text/plain",
                False,
                id="cannot-produce-overrides-always",
            ),
            pytest.param(
                "always",
                False,
                True,
                "application/pdf",
                True,
                id="requires-rendition-wins-even-if-cannot-produce",
            ),
            pytest.param(
                "auto",
                True,
                False,
                "image/tiff",
                True,
                id="auto-image-returns-true",
            ),
            pytest.param(
                "auto",
                True,
                False,
                "message/rfc822",
                False,
                id="auto-non-pdf-non-image-returns-false",
            ),
        ],
    )
    def test_generation_setting(
        self,
        settings,
        generation: str,
        can_produce: bool,  # noqa: FBT001
        requires_rendition: bool,  # noqa: FBT001
        mime: str,
        expected: bool,  # noqa: FBT001
    ) -> None:
        settings.ARCHIVE_FILE_GENERATION = generation
        parser = _parser_instance(
            can_produce=can_produce,
            requires_rendition=requires_rendition,
        )
        assert should_produce_archive(parser, mime, Path("/tmp/doc")) is expected

    @pytest.mark.parametrize(
        ("extracted_text", "expected"),
        [
            pytest.param(
                "This is a born-digital PDF with lots of text content. " * 10,
                False,
                id="born-digital-long-text-skips-archive",
            ),
            pytest.param(None, True, id="no-text-scanned-produces-archive"),
            pytest.param("tiny", True, id="short-text-treated-as-scanned"),
        ],
    )
    def test_auto_pdf_archive_decision(
        self,
        mocker: MockerFixture,
        settings,
        extracted_text: str | None,
        expected: bool,  # noqa: FBT001
    ) -> None:
        settings.ARCHIVE_FILE_GENERATION = "auto"
        mocker.patch("documents.consumer.is_tagged_pdf", return_value=False)
        mocker.patch("documents.consumer.extract_pdf_text", return_value=extracted_text)
        parser = _parser_instance(can_produce=True, requires_rendition=False)
        assert (
            should_produce_archive(parser, "application/pdf", Path("/tmp/doc.pdf"))
            is expected
        )

    def test_tagged_pdf_skips_archive_in_auto_mode(
        self,
        mocker: MockerFixture,
        settings,
    ) -> None:
        """Tagged PDFs (e.g. Word exports) with real text are treated as born-digital, even below PDF_TEXT_MIN_LENGTH."""
        settings.ARCHIVE_FILE_GENERATION = "auto"
        mocker.patch("documents.consumer.is_tagged_pdf", return_value=True)
        mocker.patch("documents.consumer.extract_pdf_text", return_value="tiny")
        parser = _parser_instance(can_produce=True, requires_rendition=False)
        assert (
            should_produce_archive(parser, "application/pdf", Path("/tmp/doc.pdf"))
            is False
        )

    def test_tagged_pdf_without_text_produces_archive(
        self,
        mocker: MockerFixture,
        settings,
    ) -> None:
        """A tagged PDF with no actual extractable text (e.g. some scanner firmware) is not
        trusted as born-digital — the tag alone must not bypass OCR."""
        settings.ARCHIVE_FILE_GENERATION = "auto"
        mocker.patch("documents.consumer.is_tagged_pdf", return_value=True)
        mocker.patch("documents.consumer.extract_pdf_text", return_value=None)
        parser = _parser_instance(can_produce=True, requires_rendition=False)
        assert (
            should_produce_archive(parser, "application/pdf", Path("/tmp/doc.pdf"))
            is True
        )
