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

PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

if 'fitz' not in sys.modules:
    sys.modules['fitz'] = types.ModuleType('fitz')

if 'openai' not in sys.modules:
    openai_mod = types.ModuleType('openai')
    openai_mod.OpenAI = type('OpenAIStub', (), {})
    sys.modules['openai'] = openai_mod

if 'google' not in sys.modules:
    google_mod = types.ModuleType('google')
    genai_mod = types.ModuleType('google.genai')
    google_mod.genai = genai_mod
    sys.modules['google'] = google_mod
    sys.modules['google.genai'] = genai_mod

if 'pymupdf4llm' not in sys.modules:
    pymod = types.ModuleType('pymupdf4llm')
    pymod.to_markdown = lambda pdf_path, **kwargs: '# Dummy'
    sys.modules['pymupdf4llm'] = pymod

if 'rapidocr_onnxruntime' not in sys.modules:
    ro_mod = types.ModuleType('rapidocr_onnxruntime')
    class RapidOCR:
        def __init__(self):
            pass
        def __call__(self, data):
            return ([], None)
    ro_mod.RapidOCR = RapidOCR
    sys.modules['rapidocr_onnxruntime'] = ro_mod

import app.services.ingestion as orch
from app.services.parser.smart_chunker import build_llm_context_from_chunks, generate_chunks


sample_markdown = """
# Paper Title

## Introduction
This is the introduction section with useful context.
It should be kept for LLM processing.

## Methodology
This section describes the method in detail.
More relevant content lives here.

## References
[1] Example reference entry that must not reach the LLM.
[2] Another reference entry.

## Appendix
Extra appendix content that must be filtered out.
""" + ("\nReference filler line.\n" * 800)

print('Task 5 filtering tests start')

chunks = generate_chunks(sample_markdown, file_hash='task5_hash', title='Task 5 Paper', chunk_size=500, chunk_overlap=50)
print(f'generated_chunks={len(chunks)}')
if not chunks:
    raise AssertionError('generate_chunks returned no chunks')

joined_text, filtered_chunks = build_llm_context_from_chunks(chunks, max_chars=25000)
print(f'filtered_chunks={len(filtered_chunks)}')
print(f'joined_text_length={len(joined_text)}')

if 'References' in joined_text or 'Appendix' in joined_text or 'reference filler' in joined_text.lower():
    raise AssertionError('Filtered LLM context still contains irrelevant sections')

if len(joined_text) > 25000:
    raise AssertionError('LLM context exceeds 25000 characters')

if not any('Introduction' in c.get('metadata_header', '') or 'Methodology' in c.get('metadata_header', '') for c in filtered_chunks):
    raise AssertionError('Relevant chunks were not preserved')

fake_pdf = PROJECT_ROOT / 'scripts' / 'task5_dummy.pdf'
fake_pdf.write_bytes(b'%PDF-1.4\n%EOF')

captured_text = {'value': None}

with patch('app.services.ingestion.check_node_exists', return_value=False), \
     patch.object(orch.hybrid_parser, 'parse_pdf', return_value=sample_markdown), \
     patch('app.services.ingestion.extract_pillars_with_llm', side_effect=lambda text, client: captured_text.__setitem__('value', text) or {
         'background': 'background text',
         'methodology': 'methodology text',
         'gap': 'gap text',
         'objective': 'objective text',
         'futurework': 'futurework text',
     }), \
     patch('app.services.ingestion.embed_text', return_value=[0.1] * 768), \
     patch('app.services.ingestion.create_literature_node', return_value='neo4j-node-1'), \
     patch.object(orch.task_queue, 'put') as p_put:
    result = orch.process_sync_pdf(str(fake_pdf), driver=MagicMock(), or_client=MagicMock(), gemini_client=MagicMock())
    print(f'process_sync_pdf_result={result}')
    print(f'queue_put_called={p_put.called}')

if captured_text['value'] is None:
    raise AssertionError('LLM text capture did not run')

print(f'llm_text_length={len(captured_text["value"])}')
if 'References' in captured_text['value'] or 'Appendix' in captured_text['value']:
    raise AssertionError('process_sync_pdf passed irrelevant text to the LLM')

if len(captured_text['value']) > 25000:
    raise AssertionError('process_sync_pdf passed more than 25000 characters to the LLM')

print('Task 5 filtering tests passed')
