"""Runtime del flujo de carga individual aislado del batch masivo."""

from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from threading import Thread

from app.batch_processing.infrastructure.heuristic_association import HeuristicCaseAssociationService
from app.batch_processing.infrastructure.heuristic_classifier import HeuristicDocumentClassifier
from app.batch_processing.infrastructure.pdf_text_extractor import PdfTextExtractor
from app.core.logging import get_log_context, set_log_context
from app.individual_ingestion.application.use_cases import (
    CancelIndividualUploadUseCase,
    ConfirmIndividualUploadUseCase,
    CreateIndividualUploadUseCase,
    GetIndividualUploadStatusUseCase,
    GetLatestPendingIndividualUploadUseCase,
    RunIndividualMaterializationUseCase,
    RunIndividualPrecheckUseCase,
)
from app.individual_ingestion.infrastructure.local_artifacts import LocalIndividualUploadFileStore
from app.individual_ingestion.infrastructure.mongo_repositories import MongoIndividualUploadRepository
from app.services.clinical_document_service import ClinicalDocumentService


class InProcessIndividualDispatcher:
    def __init__(self, *, precheck_runner, materialization_runner) -> None:
        self.precheck_runner = precheck_runner
        self.materialization_runner = materialization_runner

    def dispatch_precheck(self, upload_id: str) -> None:
        self._spawn(self.precheck_runner, upload_id)

    def dispatch_materialization(self, upload_id: str) -> None:
        self._spawn(self.materialization_runner, upload_id)

    def _spawn(self, runner, upload_id: str) -> None:
        audit_context = get_log_context()
        thread = Thread(target=self._safe_run, args=(runner, upload_id, audit_context), daemon=True)
        thread.start()

    def _safe_run(self, runner, upload_id: str, audit_context: dict[str, object]) -> None:
        set_log_context(audit_context)
        runner(upload_id)


class CeleryIndividualDispatcher:
    def dispatch_precheck(self, upload_id: str) -> None:
        from app.batch_processing.celery_app import precheck_individual_upload_job

        precheck_individual_upload_job.apply_async(
            args=[upload_id, get_log_context()], queue="individual_uploads"
        )

    def dispatch_materialization(self, upload_id: str) -> None:
        from app.batch_processing.celery_app import materialize_individual_upload_job

        materialize_individual_upload_job.apply_async(
            args=[upload_id, get_log_context()],
            queue="individual_uploads",
        )


@dataclass
class IndividualIngestionRuntime:
    create_upload: CreateIndividualUploadUseCase
    get_upload_status: GetIndividualUploadStatusUseCase
    get_latest_pending: GetLatestPendingIndividualUploadUseCase
    confirm_upload: ConfirmIndividualUploadUseCase
    cancel_upload: CancelIndividualUploadUseCase
    run_precheck: RunIndividualPrecheckUseCase
    run_materialization: RunIndividualMaterializationUseCase
    dispatcher_name: str


def build_individual_ingestion_runtime(
    base_dir: Path,
    colombia_tz,
    *,
    clinical_document_service: ClinicalDocumentService,
    mongo_database=None,
) -> IndividualIngestionRuntime:
    repository = MongoIndividualUploadRepository(database=mongo_database)
    file_store = LocalIndividualUploadFileStore(base_dir)
    text_extractor = PdfTextExtractor()
    classifier = HeuristicDocumentClassifier()
    association_service = HeuristicCaseAssociationService()

    dispatcher_mode = os.getenv("INDIVIDUAL_INGESTION_DISPATCHER", os.getenv("BATCH_DISPATCHER", "inprocess"))
    dispatcher_mode = dispatcher_mode.strip().lower()

    run_materialization = RunIndividualMaterializationUseCase(
        repository=repository,
        file_store=file_store,
        clinical_document_service=clinical_document_service,
        colombia_tz=colombia_tz,
    )

    if dispatcher_mode == "celery":
        dispatcher = CeleryIndividualDispatcher()
    else:
        dispatcher = InProcessIndividualDispatcher(
            precheck_runner=None,
            materialization_runner=run_materialization.execute,
        )
        dispatcher_mode = "inprocess"

    run_precheck = RunIndividualPrecheckUseCase(
        repository=repository,
        file_store=file_store,
        text_extractor=text_extractor,
        classifier=classifier,
        clinical_document_service=clinical_document_service,
        case_association_service=association_service,
        dispatcher=dispatcher,
        colombia_tz=colombia_tz,
    )
    if isinstance(dispatcher, InProcessIndividualDispatcher):
        dispatcher.precheck_runner = run_precheck.execute

    create_upload = CreateIndividualUploadUseCase(
        repository=repository,
        file_store=file_store,
        dispatcher=dispatcher,
        colombia_tz=colombia_tz,
    )
    get_upload_status = GetIndividualUploadStatusUseCase(repository=repository)
    get_latest_pending = GetLatestPendingIndividualUploadUseCase(repository=repository)
    confirm_upload = ConfirmIndividualUploadUseCase(
        repository=repository,
        dispatcher=dispatcher,
        colombia_tz=colombia_tz,
    )
    cancel_upload = CancelIndividualUploadUseCase(
        repository=repository,
        file_store=file_store,
        colombia_tz=colombia_tz,
    )

    return IndividualIngestionRuntime(
        create_upload=create_upload,
        get_upload_status=get_upload_status,
        get_latest_pending=get_latest_pending,
        confirm_upload=confirm_upload,
        cancel_upload=cancel_upload,
        run_precheck=run_precheck,
        run_materialization=run_materialization,
        dispatcher_name=dispatcher_mode,
    )
