"""Unit tests for the legacy engine adapter (lightrag.parser.legacy.parser).

Covers the worker-stage extraction contract directly: success path
(persist + archive + RAW ParseResult), the unsupported-suffix gate, the
whitespace-only extraction guard (scanned-PDF case) and the
``PDF_DECRYPT_PASSWORD`` plumbing.
"""

import pytest

import lightrag.pipeline as _pipeline
from lightrag.constants import FULL_DOCS_FORMAT_RAW
from lightrag.parser.base import ParseContext
from lightrag.parser.legacy.extractors import LegacyExtractionError
from lightrag.parser.legacy.parser import LegacyParser

pytestmark = pytest.mark.offline


class _FakeRag:
    def __init__(self):
        self.persisted = []

    async def _persist_parsed_full_docs(self, doc_id, payload):
        self.persisted.append((doc_id, payload))

    def _resolve_source_file_for_parser(
        self, file_path, *, source_file=None, parser_engine=None
    ):
        return file_path


@pytest.fixture
def archived(monkeypatch):
    """Record archive calls instead of moving files into __parsed__."""
    calls = []

    async def _record(source_path):
        calls.append(source_path)
        return source_path

    monkeypatch.setattr(_pipeline, "archive_docx_source_after_full_docs_sync", _record)
    return calls


def _ctx(rag, source_path):
    return ParseContext(rag, "doc-legacy", str(source_path), {})


async def test_legacy_parse_txt_persists_and_archives(tmp_path, archived):
    source = tmp_path / "notes.txt"
    source.write_text("plain text body", encoding="utf-8")
    rag = _FakeRag()

    result = await LegacyParser().parse(_ctx(rag, source))

    assert result.parse_format == FULL_DOCS_FORMAT_RAW
    assert result.content == "plain text body"
    assert result.parse_engine == "legacy"
    assert result.blocks_path == ""
    doc_id, payload = rag.persisted[0]
    assert doc_id == "doc-legacy"
    assert payload["content"] == "plain text body"
    assert payload["parse_format"] == FULL_DOCS_FORMAT_RAW
    assert archived == [str(source)]
    # to_dict stays byte-compatible: no spurious skip/warning keys.
    assert "parse_stage_skipped" not in result.to_dict()


async def test_legacy_parse_unsupported_suffix_raises(tmp_path, archived):
    source = tmp_path / "image.xyz"
    source.write_bytes(b"not parseable")
    rag = _FakeRag()

    with pytest.raises(ValueError, match=r"does not support \.xyz"):
        await LegacyParser().parse(_ctx(rag, source))

    assert rag.persisted == []
    assert archived == []


async def test_legacy_parse_whitespace_only_extraction_raises(
    tmp_path, archived, monkeypatch
):
    # A scanned PDF (no text layer) extracts to pure whitespace; the parser
    # must fail the doc instead of persisting an empty document.
    monkeypatch.setattr(
        "lightrag.parser.legacy.extractors.extract_text",
        lambda file_bytes, suffix, *, pdf_password=None: "\n \n\t\n",
    )
    source = tmp_path / "scanned.pdf"
    source.write_bytes(b"%PDF-fake")
    rag = _FakeRag()

    with pytest.raises(LegacyExtractionError, match="no usable text"):
        await LegacyParser().parse(_ctx(rag, source))

    assert rag.persisted == []
    assert archived == []


async def test_legacy_parse_passes_pdf_password_from_env(
    tmp_path, archived, monkeypatch
):
    seen = {}

    def _capture(file_bytes, suffix, *, pdf_password=None):
        seen["suffix"] = suffix
        seen["pdf_password"] = pdf_password
        return "decrypted text"

    monkeypatch.setattr("lightrag.parser.legacy.extractors.extract_text", _capture)
    monkeypatch.setenv("PDF_DECRYPT_PASSWORD", "s3cret")
    source = tmp_path / "locked.pdf"
    source.write_bytes(b"%PDF-fake")
    rag = _FakeRag()

    result = await LegacyParser().parse(_ctx(rag, source))

    assert seen == {"suffix": "pdf", "pdf_password": "s3cret"}
    assert result.content == "decrypted text"
