"""Document processing pipeline: local OCR → extraction → firm review.

Runs asynchronously after upload so the upload API returns quickly.
Uses LocalTesseractOCRProvider (free, no GPU, no paid APIs).
"""

from __future__ import annotations

import logging
import threading

logger = logging.getLogger(__name__)

_lock = threading.Lock()
_inflight: set[str] = set()


def enqueue_pending_documents(*, limit: int = 20) -> int:
    """
    Re-queue documents stuck in Processing (e.g. after server restart).

    Safe to call on startup. Skips invoices already being processed.
    """
    from app.services import documents as document_service
    from app.services.invoice_workflow import STATUS_NEW, STATUS_PROCESSING
    from app.store import store

    queued = 0
    for invoice in list(store.state.invoices):
        if queued >= limit:
            break
        if invoice.status not in {STATUS_PROCESSING, STATUS_NEW}:
            continue
        ocr_status = (invoice.ocrStatus or "").lower()

        if ocr_status == "completed" and invoice.processedAt:
            continue

        if ocr_status == "completed" and not invoice.processedAt:
            try:
                store.mark_ready_for_review(invoice.id, confidence=invoice.confidence)
                queued += 1
            except Exception:
                logger.exception("Failed to finalize OCR for %s", invoice.id)
            continue

        # pending / legacy null — only if the original file is still on disk
        if ocr_status in {"pending", ""}:
            path = document_service.resolve_document_path(invoice.storageKey)
            if not path:
                continue
            enqueue_document_processing(invoice.id)
            queued += 1
    if queued:
        logger.info("OCR recovery queued/finalized %s document(s)", queued)
    return queued


def ocr_pipeline_available() -> bool:
    """Return True when the local OCR engine can run."""
    try:
        from app.services import ocr_service

        return ocr_service.ocr_engine_available()
    except Exception:
        logger.exception("OCR availability check failed")
        return False


def enqueue_document_processing(invoice_id: str) -> None:
    """
    Queue OCR/extraction for a document without blocking the upload response.

    Uses a daemon thread (no Redis/Celery required for local development).
    Duplicate enqueues for the same invoice are ignored while a job is running.
    """
    with _lock:
        if invoice_id in _inflight:
            logger.info("OCR already in flight for %s — skip duplicate enqueue", invoice_id)
            return
        _inflight.add(invoice_id)

    thread = threading.Thread(
        target=_run_processing_job,
        args=(invoice_id,),
        name=f"ocr-{invoice_id}",
        daemon=True,
    )
    thread.start()
    logger.info("OCR job queued for %s", invoice_id)


def _run_processing_job(invoice_id: str) -> None:
    try:
        _process_document(invoice_id)
    except Exception:
        logger.exception("Unhandled OCR job failure for %s", invoice_id)
        try:
            from app.store import store

            store.mark_ocr_failed(
                invoice_id,
                error="Document processing failed unexpectedly.",
            )
        except Exception:
            logger.exception("Could not mark OCR failure for %s", invoice_id)
    finally:
        with _lock:
            _inflight.discard(invoice_id)


def _process_document(invoice_id: str) -> None:
    from app.services import documents as document_service
    from app.services import ocr_service
    from app.store import store

    try:
        invoice = store._find_invoice(invoice_id)  # noqa: SLF001
    except KeyError:
        logger.warning("OCR job: invoice %s not found", invoice_id)
        return

    store.log_ocr_event(invoice_id, "OCR processing started")

    if not ocr_service.ocr_engine_available():
        logger.warning("Local OCR unavailable — invoice %s needs manual review", invoice_id)
        store.mark_ocr_failed(
            invoice_id,
            error="Local OCR engine is not installed or unavailable.",
        )
        return

    path = document_service.resolve_document_path(invoice.storageKey)
    if not path:
        store.mark_ocr_failed(
            invoice_id,
            error="Uploaded file could not be found for OCR.",
        )
        return

    result = ocr_service.process_document(path, invoice.mimeType)

    if result.status == "unavailable":
        store.mark_ocr_failed(
            invoice_id,
            error=result.error or "Local OCR engine is not installed or unavailable.",
        )
        return

    if result.status == "failed":
        store.apply_ocr_result(invoice_id, result)
        store.mark_ocr_failed(
            invoice_id,
            error=result.error or "Local OCR could not read this document.",
        )
        return

    # completed
    store.apply_ocr_result(invoice_id, result)
    confidence = result.extraction.overall_confidence if result.extraction else 0.0
    if confidence < 60:
        store.log_ocr_event(invoice_id, "OCR extraction requires review")
    else:
        store.log_ocr_event(
            invoice_id,
            f"OCR processing completed ({result.ocr_engine}, {int(confidence)}% confidence)",
        )
    store.mark_ready_for_review(invoice_id, confidence=confidence)
