# -*- coding: utf-8 -*-
"""PDF 추출기: 텍스트 추출 → 표제란 키워드(REV, DWG NO, PROJECT, DATE) 파싱.

도면이 DXF가 아니라 PDF로만 도는 현장 대비 (LOG 2026-08-21 결정).
엔진: pypdfium2 (구글 PDFium, BSD/Apache) — 판매 제품에 AGPL(PyMuPDF)을
쓸 수 없어 교체 (2026-08-26 결정, 원래 스택 원칙인 허용적 라이선스로 복귀).
"""

from __future__ import annotations

import re
from pathlib import Path

import pypdfium2 as pdfium

from watcher.extract.base import Document, Field, TextLine

# "DWG NO: DWG-P-301" / "REV : A" 꼴의 표제란 줄
_FIELD_RE = re.compile(
    r"^\s*(DWG[ ._]?NO|DRAWING NO|도면번호|PROJECT|프로젝트|REV(?:ISION)?|리비전|DATE|일자)"
    r"\s*[:：]\s*(.+?)\s*$",
    re.IGNORECASE,
)
_CANON = {
    "DWG NO": "DWG_NO", "DWGNO": "DWG_NO", "DRAWING NO": "DWG_NO", "도면번호": "DWG_NO",
    "PROJECT": "PROJECT", "프로젝트": "PROJECT",
    "REV": "REV", "REVISION": "REV", "리비전": "REV",
    "DATE": "DATE", "일자": "DATE",
}

# PDFium 페이지 객체 유형 상수 (FPDF_PAGEOBJ_*)
_OBJ_TEXT, _OBJ_PATH, _OBJ_IMAGE = 1, 2, 3

# 페이지에 그림·이미지는 있는데 추출된 글자가 이보다 적으면 '텍스트 빈약 페이지'.
# CAD가 SHX 글꼴을 선(벡터)으로 변환해 출력한 PDF, 스캔본이 여기 해당한다.
_POOR_PAGE_CHARS = 40
_MIN_CONTENT_OBJS = 5  # 텍스트 외 객체(선·이미지)가 이보다 적으면 빈 페이지로 본다


def _canon_label(label: str) -> str | None:
    return _CANON.get(re.sub(r"[ ._]+", " ", label.upper()).strip())


def extract(path: Path) -> Document:
    pdf = pdfium.PdfDocument(path.read_bytes())  # 핸들 즉시 반납 (탐색기 간섭 방지)
    fields: dict[str, Field] = {}
    body: list[TextLine] = []
    poor_pages, imaged = 0, False
    page_count = len(pdf)
    try:
        for pno in range(1, page_count + 1):
            page = pdf[pno - 1]
            textpage = page.get_textpage()
            raw_all = textpage.get_text_bounded() or ""
            page_chars = 0
            for lno, raw in enumerate(raw_all.splitlines(), start=1):
                line = raw.strip()
                if not line:
                    continue
                page_chars += len(line)
                where = f"{pno}페이지 {lno}행"
                m = _FIELD_RE.match(line)
                canon = _canon_label(m.group(1)) if m else None
                if canon and canon not in fields:
                    fields[canon] = Field(canon, m.group(2), f"표제란({where})")
                else:
                    body.append(TextLine(line, where))
            if page_chars < _POOR_PAGE_CHARS:
                # 벡터 변환 페이지는 객체가 수만 개 — 판정에 필요한 만큼만 보고 중단
                has_image, nontext = False, 0
                for o in page.get_objects(max_depth=2):
                    if o.type == _OBJ_IMAGE:
                        has_image = True
                        break
                    if o.type != _OBJ_TEXT:
                        nontext += 1
                        if nontext >= _MIN_CONTENT_OBJS:
                            break
                if has_image or nontext >= _MIN_CONTENT_OBJS:
                    poor_pages += 1  # 내용은 그려져 있는데 글자만 못 읽는 페이지
                    imaged = imaged or has_image
            textpage.close()
            page.close()
    finally:
        pdf.close()

    notes: list[str] = []
    if poor_pages and poor_pages * 2 >= page_count:
        if imaged:
            reason = "스캔본(이미지) PDF로 보입니다"
            fix = "원본 CAD·문서에서 텍스트가 포함된 PDF로 다시 출력해야 비교할 수 있습니다"
        else:
            reason = "글자가 선(벡터)으로 변환된 출력으로 보입니다 (CAD SHX 글꼴 등)"
            fix = "CAD에서 TrueType 글꼴을 사용해 PDF로 출력하면 비교할 수 있습니다"
        notes.append(
            f"{page_count}페이지 중 {poor_pages}페이지에서 텍스트가 거의 추출되지 않았습니다"
            f" — {reason}. 이 파일은 텍스트 기반 내용 비교가 불가능합니다. {fix}.")
    return Document(doc_type="pdf", path=str(path), fields=fields,
                    raw_texts=body, notes=notes)
