"""
drawing_integrity.py - Per-sheet Drawing Integrity QA (LLM, classic pipeline).

The drawing-focused pass: reads ONE sheet's own extracted objects + sheet image
+ deterministic text layer and flags defects internal to that single sheet
(dangling detail/callout/keynote references, schedule-vs-plan/legend
disagreements on the same sheet, dimension strings that do not sum, missing
title-block/scale essentials, duplicate/inconsistent tags). It complements the
cross-sheet conflict checker; it never does code/ADA or cross-sheet
coordination. Emits the canonical issue schema. Returns [] on failure.

Gated by config.ENABLE_DRAWING_INTEGRITY. Runs sheets concurrently, one call
per sheet, skipping sheets below INTEGRITY_MIN_ASSERTIONS.
"""

from concurrent.futures import ThreadPoolExecutor
from typing import Dict, List

from backend import config
from backend.agents.prompts import (
    DRAWING_INTEGRITY_SYSTEM_PROMPT,
    DRAWING_INTEGRITY_USER_PROMPT,
)
from backend.pipeline._serialize import dumps
from backend.pipeline._stage import call_stage, collect_list, validate_issue


def _sheet_meta(sheet: Dict) -> Dict:
    return {
        "sheet_number": sheet.get("sheet_number"),
        "sheet_title": sheet.get("sheet_title"),
        "discipline": sheet.get("discipline"),
        "drawing_type": sheet.get("drawing_type"),
        "level": sheet.get("level"),
        "scale": sheet.get("scale"),
    }


def _review_sheet(sheet: Dict, page_to_b64: Dict, page_to_text: Dict) -> List[Dict]:
    page_number = sheet.get("page_number")
    assertions = (sheet.get("assertions") or [])[
        :config.AGENT_INTEGRITY_MAX_ASSERTIONS
    ]
    text_layer = (page_to_text.get(page_number) or "")[
        :config.TEXT_LAYER_MAX_CHARS
    ]
    image = page_to_b64.get(page_number)
    images = [image][:config.AGENT_INTEGRITY_MAX_IMAGES] if image else []
    parsed = call_stage(
        DRAWING_INTEGRITY_SYSTEM_PROMPT,
        DRAWING_INTEGRITY_USER_PROMPT,
        subs={
            "sheet_meta": dumps(_sheet_meta(sheet)),
            "assertions": dumps(assertions),
            "text_layer": text_layer,
        },
        images_b64=images,
        max_tokens=config.INTEGRITY_MAX_TOKENS,
    )
    issues = collect_list(
        parsed, "issues", lambda c: validate_issue(c, "drawing_integrity")
    )
    sheet_number = sheet.get("sheet_number")
    for issue in issues:
        if not issue.get("sheets") and sheet_number:
            issue["sheets"] = [sheet_number]
    return issues


def drawing_integrity_review(sheets: List[Dict], pages: List[Dict]) -> List[Dict]:
    """One LLM call per non-sparse sheet, run concurrently."""
    if not config.ENABLE_DRAWING_INTEGRITY:
        return []
    page_to_b64 = {p["page_number"]: p.get("base64") for p in pages}
    page_to_text = {p["page_number"]: p.get("text_layer") for p in pages}
    targets = [
        s for s in sheets
        if len(s.get("assertions") or []) >= config.INTEGRITY_MIN_ASSERTIONS
    ]
    issues: List[Dict] = []
    if targets:
        with ThreadPoolExecutor(max_workers=config.AGENT_INTEGRITY_CONCURRENCY) as pool:
            for res in pool.map(
                lambda s: _review_sheet(s, page_to_b64, page_to_text), targets
            ):
                issues.extend(res)
    print(f"[DrawingIntegrity] {len(issues)} issue(s) across {len(targets)} sheet(s)")
    return issues
