# Copyright (C) 2026 NPO NT
# SPDX-License-Identifier: AGPL-3.0-only

from __future__ import annotations

from .barcodes import barcode_available, detect_barcodes
from .detectors import deduplicate_findings, detect_tokens
from .loader import heif_available
from .models import AnalysisResult, CapabilityStatus, DocumentData, Finding
from .ocr import OcrError, TesseractOcr
from .progress import CancelSignal, ProgressCallback, ensure_not_cancelled, report_progress


LOW_OCR_CONFIDENCE = 60.0


def analyze_document(
    document: DocumentData,
    ocr: TesseractOcr | None = None,
    *,
    progress_callback: ProgressCallback | None = None,
    cancel_signal: CancelSignal | None = None,
) -> AnalysisResult:
    ensure_not_cancelled(cancel_signal)
    report_progress(progress_callback, "capabilities")
    ocr_engine = ocr or TesseractOcr()
    languages = ocr_engine.selected_languages if ocr_engine.available else ()
    blocking_issues: list[str] = []
    if not ocr_engine.available:
        blocking_issues.append("Локальный OCR недоступен")
    elif "rus" not in languages:
        blocking_issues.append("Не установлена русская модель OCR")
    if not barcode_available():
        blocking_issues.append("Локальный детектор QR-кодов и штрихкодов недоступен")

    capabilities = CapabilityStatus(
        ocr_available=ocr_engine.available,
        ocr_languages=languages,
        barcode_available=barcode_available(),
        heif_available=heif_available(),
        blocking_issues=blocking_issues,
    )

    findings: list[Finding] = []
    warnings: list[str] = []
    manual_pages: set[int] = set()

    page_count = len(document.pages)
    for page in document.pages:
        ensure_not_cancelled(cancel_signal)
        if page.native_tokens:
            findings.extend(detect_tokens(page.native_tokens, page.index))
        if ocr_engine.available:
            report_progress(progress_callback, "ocr", page.index + 1, page_count)
            try:
                result = ocr_engine.recognize(page.image, page.index, cancel_signal=cancel_signal)
            except OcrError as exc:
                warnings.append(f"Страница {page.index + 1}: {exc}")
                manual_pages.add(page.index)
                if "OCR завершился с ошибкой" not in capabilities.blocking_issues:
                    capabilities.blocking_issues.append("OCR завершился с ошибкой хотя бы на одной странице")
            else:
                page.ocr_tokens = result.tokens
                page.ocr_mean_confidence = result.mean_confidence
                findings.extend(detect_tokens(result.tokens, page.index))
                if result.mean_confidence is None or result.mean_confidence < LOW_OCR_CONFIDENCE:
                    warnings.append(f"Страница {page.index + 1}: низкая уверенность OCR; требуется особенно внимательная проверка")
                    manual_pages.add(page.index)
                if not result.tokens and not page.native_tokens:
                    warnings.append(f"Страница {page.index + 1}: текст не распознан")
                    manual_pages.add(page.index)
        else:
            manual_pages.add(page.index)

        ensure_not_cancelled(cancel_signal)
        if barcode_available():
            report_progress(progress_callback, "barcodes", page.index + 1, page_count)
            findings.extend(detect_barcodes(page.image, page.index))

        # Every page remains human-reviewed in v0.2; these flags highlight elevated risk.
        if page.image.width < 800 or page.image.height < 800:
            warnings.append(f"Страница {page.index + 1}: низкое разрешение изображения")
            manual_pages.add(page.index)
        report_progress(progress_callback, "page_complete", page.index + 1, page_count)

    ensure_not_cancelled(cancel_signal)
    report_progress(progress_callback, "complete", page_count, page_count)
    return AnalysisResult(
        document=document,
        findings=deduplicate_findings(findings),
        capabilities=capabilities,
        warnings=warnings,
        pages_requiring_manual_review=manual_pages,
    )
