"""OCR 子进程边界及其真实发生结果分类。"""

from __future__ import annotations

import subprocess
from collections.abc import Callable
from datetime import UTC, datetime
from pathlib import Path
from threading import Semaphore
from typing import Any

from app.core.logging import get_logger
from app.services.commercial_direct_operation import DirectOperationOutcome
from app.services.ocr_commercial import (
    OcrCommercialAttempt,
    OcrCommercialObserver,
    OcrOperationContext,
)

logger = get_logger("app.services.ocr_worker_runtime")


def invoke_ocr_worker(
    *,
    settings: Any,
    python_bin: str,
    worker_path: str,
    input_paths: list[Path],
    semaphore: Semaphore,
    parse_stdout: Callable[[str], dict | None],
    commercial_observer: OcrCommercialObserver | None,
    operation_context: OcrOperationContext | None,
) -> dict:
    started_at = datetime.now(UTC)
    command = _worker_command(
        settings=settings,
        python_bin=python_bin,
        worker_path=worker_path,
        input_paths=input_paths,
    )
    attempt = _permit_commercial(
        observer=commercial_observer,
        context=operation_context,
        page_count=len(input_paths),
        provider="PaddleOCR",
        model_name=str(settings.ocr_text_recognition_model or "PP-OCRv5_mobile"),
        started_at=started_at,
    )
    try:
        with semaphore:
            completed = subprocess.run(
                command,
                capture_output=True,
                text=True,
                timeout=settings.ocr_timeout_seconds,
                check=False,
            )
    except Exception:
        _complete_commercial(
            commercial_observer,
            attempt,
            outcome="outcome_unknown",
        )
        raise
    if completed.returncode != 0:
        _complete_commercial(
            commercial_observer,
            attempt,
            outcome="provider_rejected",
        )
        detail = (completed.stderr or completed.stdout or "").strip()
        raise RuntimeError(f"OCR 执行失败：{detail or 'worker 返回非 0 状态码。'}")
    try:
        payload = parse_stdout(completed.stdout)
    except Exception:
        _complete_commercial(
            commercial_observer,
            attempt,
            outcome="postprocess_failed",
        )
        raise
    if payload is None:
        _complete_commercial(
            commercial_observer,
            attempt,
            outcome="postprocess_failed",
        )
        raise RuntimeError("OCR worker 未返回可解析的 JSON 结果。")
    _complete_commercial(commercial_observer, attempt, outcome="succeeded")
    return payload


def _worker_command(
    *,
    settings: Any,
    python_bin: str,
    worker_path: str,
    input_paths: list[Path],
) -> list[str]:
    command = [
        python_bin,
        worker_path,
        "--lang",
        settings.ocr_language,
        "--text-detection-model",
        settings.ocr_text_detection_model,
        "--text-recognition-model",
        settings.ocr_text_recognition_model,
    ]
    configured_device = str(settings.ocr_device or "").strip()
    if configured_device:
        command.extend(["--device", configured_device])
    for path in input_paths:
        command.extend(["--input", str(path)])
    return command


def _permit_commercial(
    *,
    observer: OcrCommercialObserver | None,
    context: OcrOperationContext | None,
    page_count: int,
    provider: str,
    model_name: str,
    started_at: datetime,
) -> OcrCommercialAttempt | None:
    if observer is None or context is None:
        return None
    return observer.permit(
        context,
        page_count=page_count,
        provider=provider,
        model_name=model_name,
        started_at=started_at,
    )


def _complete_commercial(
    observer: OcrCommercialObserver | None,
    attempt: OcrCommercialAttempt | None,
    *,
    outcome: DirectOperationOutcome,
) -> None:
    if observer is None or attempt is None:
        return
    try:
        result = observer.complete(
            attempt,
            outcome=outcome,
            completed_at=datetime.now(UTC),
        )
        if result.requires_reconciliation:
            logger.warning(
                "OCR commercial reconciliation required operation_call_id=%s reason=%s",
                result.operation_call_id,
                result.reason_code,
            )
    except Exception:
        logger.exception("OCR commercial completion observer failed; worker result is preserved")
