from __future__ import annotations
import os
import re
import shutil
import subprocess
import sys
from collections import Counter
_ROMAN_1_99 = '(?=[ivxl])(?:xc|xl|l?x{0,3})(?:ix|iv|v?i{0,3})'
_PDF_PAGE_NUM = re.compile(f'^\\s*(?:\\d{{1,4}}|{_ROMAN_1_99})\\s*$', re.IGNORECASE)
_PDF_HYPHEN_WRAP = re.compile('(\\w)-\\n(\\w)')

def clean_pdftotext(text: str) -> str:
    pages = text.split('\x0c')
    if len(pages) >= 3:
        edge = Counter()
        for p in pages:
            nb = [ln.strip() for ln in p.splitlines() if ln.strip()]
            if nb:
                edge[nb[0]] += 1
                if len(nb) > 1:
                    edge[nb[-1]] += 1
        boiler = {ln for ln, c in edge.items() if c > len(pages) / 2}
        kept = []
        for p in pages:
            lines = p.splitlines()
            nb_idx = [i for i, ln in enumerate(lines) if ln.strip()]
            first = nb_idx[0] if nb_idx else None
            last = nb_idx[-1] if nb_idx else None
            for i, ln in enumerate(lines):
                if i in (first, last):
                    s = ln.strip()
                    if s in boiler or _PDF_PAGE_NUM.match(s):
                        continue
                kept.append(ln)
        text = '\n'.join(kept)
    else:
        text = text.replace('\x0c', '\n')
    return _PDF_HYPHEN_WRAP.sub('\\1\\2', text)

def extract_with_pdftotext(pdf_path: str) -> str | None:
    if not shutil.which('pdftotext'):
        return None
    try:
        pdf_path = os.path.abspath(pdf_path)
        result = subprocess.run(['pdftotext', '-layout', pdf_path, '-'], capture_output=True, text=True, timeout=120, encoding='utf-8', errors='replace')
        if result.returncode == 0 and result.stdout.strip():
            return clean_pdftotext(result.stdout)
    except Exception as e:
        print(f'  [warn] extract_with_pdftotext failed: {type(e).__name__}: {e}', file=sys.stderr)
    return None

def looks_image_only(pdf_path: str, pages: int=5) -> bool:
    if not shutil.which('pdftotext'):
        return False
    try:
        result = subprocess.run(['pdftotext', '-f', '1', '-l', str(pages), os.path.abspath(pdf_path), '-'], capture_output=True, text=True, timeout=30, encoding='utf-8', errors='replace')
        return result.returncode == 0 and (not result.stdout.strip())
    except Exception:
        return False

def extract_with_pypdf(pdf_path: str) -> str | None:
    try:
        import pypdf
        text_parts = []
        with open(pdf_path, 'rb') as f:
            reader = pypdf.PdfReader(f)
            for page in reader.pages:
                try:
                    text_parts.append(page.extract_text() or '')
                except Exception:
                    text_parts.append('')
        return clean_pdftotext('\x0c'.join(text_parts))
    except ImportError:
        return None
    except Exception as e:
        print(f'  [warn] extract_with_pypdf failed: {type(e).__name__}: {e}', file=sys.stderr)
        return None

def extract_with_pdfminer(pdf_path: str) -> str | None:
    try:
        from pdfminer.high_level import extract_text
        text = extract_text(pdf_path)
        return clean_pdftotext(text) if text else text
    except ImportError:
        return None
    except Exception as e:
        print(f'  [warn] extract_with_pdfminer failed: {type(e).__name__}: {e}', file=sys.stderr)
        return None

def extract_with_docling(pdf_path: str) -> str | None:
    try:
        from docling.document_converter import DocumentConverter
        from docling.datamodel.pipeline_options import PdfPipelineOptions
        from docling.datamodel.base_models import InputFormat
        from docling.document_converter import PdfFormatOption
        pipeline_options = PdfPipelineOptions()
        pipeline_options.do_ocr = False
        pipeline_options.do_table_structure = True
        converter = DocumentConverter(format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)})
        result = converter.convert(pdf_path)
        return result.document.export_to_markdown()
    except ImportError:
        return None
    except Exception as e:
        print(f'  [warn] extract_with_docling failed: {type(e).__name__}: {e}', file=sys.stderr)
        return None

def count_pages(pdf_path: str) -> int:
    if shutil.which('pdfinfo'):
        try:
            pdf_path = os.path.abspath(pdf_path)
            result = subprocess.run(['pdfinfo', pdf_path], capture_output=True, text=True, timeout=15)
            for line in result.stdout.splitlines():
                if line.startswith('Pages:'):
                    return int(line.split(':')[1].strip())
        except Exception:
            pass
    try:
        import pypdf
        with open(pdf_path, 'rb') as f:
            return len(pypdf.PdfReader(f).pages)
    except Exception:
        return 0
