"""Dispatchers de trabajos batch: local para desarrollo y Celery para workers reales."""

from __future__ import annotations

from threading import Thread
from typing import Callable
import logging

from app.batch_processing.domain.ports import BatchJobDispatcher


logger = logging.getLogger(__name__)


class InProcessBatchJobDispatcher(BatchJobDispatcher):
    """Ejecuta el lote en un thread daemon dentro del mismo proceso web."""

    def __init__(
        self,
        *,
        prepare_runner: Callable[[str], list[str]],
        file_runner: Callable[[str, str], None],
        finalize_runner: Callable[[str], list[str]],
        queue_materialize_runner: Callable[[str, str], None],
        materialize_runner: Callable[[str, str], None],
    ) -> None:
        self.prepare_runner = prepare_runner
        self.file_runner = file_runner
        self.finalize_runner = finalize_runner
        self.queue_materialize_runner = queue_materialize_runner
        self.materialize_runner = materialize_runner

    def dispatch(self, batch_id: str) -> None:
        thread = Thread(target=self._safe_run, args=(batch_id,), daemon=True)
        thread.start()

    def _safe_run(self, batch_id: str) -> None:
        try:
            file_ids = self.prepare_runner(batch_id)
            for file_id in file_ids:
                self.file_runner(batch_id, file_id)
            materialize_ids = self.finalize_runner(batch_id)
            for file_id in materialize_ids:
                self.queue_materialize_runner(batch_id, file_id)
                self.materialize_runner(batch_id, file_id)
        except Exception:
            logger.exception("Fallo el dispatcher local del lote %s", batch_id)


class CeleryBatchJobDispatcher(BatchJobDispatcher):
    """Delegación a Celery cuando existe Redis y worker externo."""

    def dispatch(self, batch_id: str) -> None:
        from app.batch_processing.celery_app import process_batch_job

        process_batch_job.delay(batch_id)
