"""Orchestration d'extraction pour un dossier OGC.

Chaîne les étages du pipeline sans connaître leur implémentation interne :

  ingest → routing → OCR page par page → enrichissement page-spécifique → validation ATIH

L'orchestration elle-même ne contient aucune logique métier : elle délègue à
`pipeline.recueil`, `pipeline.validation`, `pipeline.classify`, `pipeline.ocr_qwen`
et `pipeline.prompts`. Cela permet de tester indépendamment chaque étage.
"""
from __future__ import annotations

import time
from pathlib import Path

from .classify import detect_page_type, route_by_index
from .ingest import pdf_to_images
from .json_utils import parse_json_output
from .ocr_qwen import QwenVLOCR
from .prompts import PAGE_TYPES, PROMPT_HEADER
from .recueil import enrich_recueil, resolve_recueil_zones
from .validation import annotate as validate_annotate


def _run_page_ocr(ocr: QwenVLOCR, image_path: Path, ptype: str) -> tuple[dict | None, str, float]:
    """Exécute le prompt principal associé à un type de page et parse le JSON.

    Retourne (parsed_dict_ou_None, ocr_raw, elapsed_s). `parsed=None` quand
    la page n'a pas de prompt structuré associé (concertation_med, hospit.).
    """
    conf = PAGE_TYPES.get(ptype)
    if not conf or conf["prompt"] == PROMPT_HEADER:
        return None, "", 0.0
    res = ocr.run(image_path, conf["prompt"], max_new_tokens=4096)
    parsed = parse_json_output(res["text"])
    return parsed, res["text"], round(res["elapsed_s"], 2)


def _resolve_routing(images: list[Path], ocr: QwenVLOCR,
                     use_standard_routing: bool,
                     verbose: bool) -> tuple[list[str | None], list[str]]:
    """Détermine le type de chaque page, soit par ordre standard (une seule
    vérification sur la page 1), soit par classification OCR page par page.

    Retourne (page_types, headers). `headers[i]` est vide si pas de classify
    effectuée sur la page i.
    """
    page_types: list[str | None] = [None] * len(images)
    headers: list[str] = [""] * len(images)

    if use_standard_routing and images:
        ptype1, header1 = detect_page_type(images[0], ocr)
        if ptype1 == "recueil":
            page_types = list(route_by_index(len(images)))
            headers[0] = header1
            if verbose:
                print("  routing standard (page 1 = recueil OK)")
            return page_types, headers
        if verbose:
            print(f"  page 1 = {ptype1} → fallback classification")

    # Fallback : classify page par page
    for i, img in enumerate(images):
        page_types[i], headers[i] = detect_page_type(img, ocr)
    return page_types, headers


def extract_dossier(pdf_path: str | Path, verbose: bool = True,
                    use_standard_routing: bool = True) -> dict:
    """Pipeline complet d'un dossier : PDF → JSON structuré + annoté ATIH.

    Étages :
    1. `ingest.pdf_to_images`    : PDF → PNG 300 dpi (avec deskew auto, cache)
    2. `_resolve_routing`        : type de chaque page
    3. `_run_page_ocr`           : OCR du schéma structuré par type de page
    4. `recueil.enrich_recueil`  : checkboxes + crop Recodage pour la page recueil
    5. `validation.annotate`     : validation ATIH de tous les codes extraits

    Paramètre `use_standard_routing=True` exploite l'ordre standard des 6 pages
    OGC et économise 5 appels OCR par dossier. Bascule automatique sur la
    classification page-à-page si la page 1 n'est pas le recueil attendu.
    """
    pdf_path = Path(pdf_path)
    ocr = QwenVLOCR()
    if verbose:
        print(f"[{pdf_path.name}] modèle prêt, VRAM={ocr.vram_gb:.2f} Go")

    images = pdf_to_images(str(pdf_path))
    if verbose:
        print(f"[{pdf_path.name}] {len(images)} pages converties")

    page_types, headers = _resolve_routing(images, ocr, use_standard_routing, verbose)

    _, cb_zones = resolve_recueil_zones()

    result: dict = {
        "fichier": pdf_path.stem,
        "pdf_hash": images[0].parent.name if images else "",
        "pages": [],
        "extraction": {},
    }

    for idx, img_path in enumerate(images, 1):
        t0 = time.time()
        ptype = page_types[idx - 1]
        header_text = headers[idx - 1]
        page_info: dict = {
            "page": idx,
            "type": ptype,
            "header": header_text.strip(),
            "elapsed_s": None,
        }
        if verbose:
            print(f"  p{idx}: {ptype}")

        parsed, ocr_raw, elapsed = _run_page_ocr(ocr, img_path, ptype) if ptype else (None, "", 0.0)
        if parsed is not None:
            page_info["ocr_raw"] = ocr_raw
            page_info["parsed"] = parsed
            page_info["elapsed_s"] = elapsed

            if ptype == "recueil" and isinstance(parsed, dict):
                enrich_recueil(parsed, img_path, ocr, cb_zones)
                page_info["parsed"] = parsed

            result["extraction"][ptype] = parsed
        else:
            page_info["elapsed_s"] = round(time.time() - t0, 2)

        result["pages"].append(page_info)

    return validate_annotate(result)
