
from __future__ import annotations
from pathlib import Path
import html
import re
import subprocess
import tempfile
import zipfile
import xml.etree.ElementTree as ET

TEXT_EXTS = {'.txt', '.md', '.tex', '.log', '.csv', '.json', '.xml', '.rtf'}
HTML_EXTS = {'.html', '.htm', '.xhtml'}
IMAGE_EXTS = {'.jpg', '.jpeg', '.png', '.tif', '.tiff', '.bmp', '.webp'}
WORD_EXTS = {'.docx', '.doc'}
PDF_EXTS = {'.pdf'}
SUPPORTED_EXTS = TEXT_EXTS | HTML_EXTS | IMAGE_EXTS | WORD_EXTS | PDF_EXTS | {'.odt', '.epub'}


def strip_html(raw: str) -> str:
    raw = re.sub(r'(?is)<(script|style).*?</\1>', ' ', raw)
    raw = re.sub(r'(?s)<[^>]+>', ' ', raw)
    return re.sub(r'\s+', ' ', html.unescape(raw)).strip()


def read_docx_text(path: Path) -> str:
    parts = []
    with zipfile.ZipFile(path) as z:
        names = [n for n in z.namelist() if n.startswith('word/') and n.endswith('.xml') and ('document' in n or 'header' in n or 'footer' in n)]
        for n in names:
            try:
                xml = z.read(n)
                root = ET.fromstring(xml)
                for node in root.iter():
                    if node.tag.endswith('}t') and node.text:
                        parts.append(node.text)
                    elif node.tag.endswith('}tab'):
                        parts.append('\t')
                    elif node.tag.endswith('}br'):
                        parts.append('\n')
            except Exception:
                continue
    return ' '.join(parts)


def read_pdf_text(path: Path) -> str:
    try:
        from pypdf import PdfReader
        r = PdfReader(str(path))
        return '\n'.join(page.extract_text() or '' for page in r.pages)
    except Exception:
        pass
    try:
        out = subprocess.check_output(['pdftotext', str(path), '-'], stderr=subprocess.DEVNULL, timeout=60)
        return out.decode('utf-8', errors='ignore')
    except Exception:
        return ''


def read_legacy_doc_text(path: Path) -> str:
    # First try antiword/catdoc if installed. Then LibreOffice conversion to txt.
    for tool in ['antiword', 'catdoc']:
        try:
            out = subprocess.check_output([tool, str(path)], stderr=subprocess.DEVNULL, timeout=60)
            return out.decode('utf-8', errors='ignore')
        except Exception:
            pass
    try:
        with tempfile.TemporaryDirectory() as td:
            subprocess.run(['soffice', '--headless', '--convert-to', 'txt:Text', '--outdir', td, str(path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=90)
            txts = list(Path(td).glob('*.txt'))
            if txts:
                return txts[0].read_text(encoding='utf-8', errors='ignore')
    except Exception:
        return ''
    return ''


def read_image_ocr(path: Path, lang: str = 'rus+eng') -> str:
    # OCR is optional and executed only when user imports scan/photo. Requires tesseract + pytesseract.
    try:
        from PIL import Image
        import pytesseract
        return pytesseract.image_to_string(Image.open(path), lang=lang)
    except Exception:
        return f'[OCR не выполнен автоматически: {path.name}. Установите tesseract/pytesseract или подключите облачный OCR.]'


def read_odt_text(path: Path) -> str:
    try:
        with zipfile.ZipFile(path) as z:
            xml = z.read('content.xml').decode('utf-8', errors='ignore')
            return strip_html(xml)
    except Exception:
        return ''


def read_epub_text(path: Path) -> str:
    parts = []
    try:
        with zipfile.ZipFile(path) as z:
            for n in z.namelist():
                if n.lower().endswith(('.html', '.xhtml', '.htm')):
                    parts.append(strip_html(z.read(n).decode('utf-8', errors='ignore')))
    except Exception:
        pass
    return '\n'.join(parts)


def read_any_document(path, lang: str = 'ru') -> dict:
    p = Path(path)
    ext = p.suffix.lower()
    text = ''
    method = 'fallback'
    if ext in TEXT_EXTS:
        text = p.read_text(encoding='utf-8', errors='ignore')
        method = 'text'
    elif ext in HTML_EXTS:
        text = strip_html(p.read_text(encoding='utf-8', errors='ignore'))
        method = 'html'
    elif ext == '.docx':
        text = read_docx_text(p)
        method = 'docx'
    elif ext == '.doc':
        text = read_legacy_doc_text(p)
        method = 'doc'
    elif ext in PDF_EXTS:
        text = read_pdf_text(p)
        method = 'pdf'
    elif ext in IMAGE_EXTS:
        text = read_image_ocr(p, 'rus+eng' if lang == 'ru' else 'eng')
        method = 'ocr-image'
    elif ext == '.odt':
        text = read_odt_text(p)
        method = 'odt'
    elif ext == '.epub':
        text = read_epub_text(p)
        method = 'epub'
    else:
        try:
            text = p.read_text(encoding='utf-8', errors='ignore')
            method = 'unknown-text-fallback'
        except Exception:
            text = ''
            method = 'unsupported-binary'
    return {'path': str(p), 'name': p.name, 'suffix': ext, 'text': text, 'method': method, 'chars': len(text)}


def is_potential_input(path: Path) -> bool:
    if path.is_dir():
        return False
    if path.name.startswith('.'):
        return False
    return True
