"""Puertos del módulo batch para desacoplar casos de uso e infraestructura."""

from __future__ import annotations

from typing import Protocol

from app.batch_processing.domain.models import (
    ArchiveExtractionResult,
    AssociationResult,
    ExtractedSignals,
    PrefacturaPageClassification,
    PrefacturaPdfPage,
)
from app.clinical_pipeline.domain.models import DocumentClassificationDecision, PdfExtractionResult


class BatchRepository(Protocol):
    """Contrato para persistencia y seguimiento del encabezado de un lote."""

    def create_batch(self, payload: dict) -> str: ...

    def update_batch(self, batch_id: str, payload: dict) -> None: ...

    def get_batch(self, batch_id: str) -> dict | None: ...

    def list_user_batches(self, username: str, *, limit: int = 20) -> list[dict]: ...

    def delete_batch(self, batch_id: str) -> int: ...

    def acquire_cases_refresh_lock(self, batch_id: str, owner: str, ttl_seconds: int) -> bool: ...

    def release_cases_refresh_lock(self, batch_id: str, owner: str) -> None: ...


class BatchFileRepository(Protocol):
    """Contrato para persistir archivos individuales pertenecientes a un lote."""

    def create_file(self, payload: dict) -> str: ...

    def update_file(self, file_id: str, payload: dict) -> None: ...

    def get_file(self, file_id: str) -> dict | None: ...

    def list_files(self, batch_id: str, *, status: str | None = None) -> list[dict]: ...

    def delete_files_by_batch(self, batch_id: str) -> int: ...


class BatchCaseRepository(Protocol):
    """Contrato para la proyección de casos consolidados generados por un lote."""

    def replace_cases(self, batch_id: str, cases: list[dict]) -> None: ...

    def list_cases(self, batch_id: str) -> list[dict]: ...

    def get_case(self, batch_id: str, case_key: str) -> dict | None: ...

    def get_user_case(self, username: str, case_key: str) -> dict | None: ...

    def list_user_cases(self, username: str, *, limit: int = 50) -> list[dict]: ...

    def update_case(self, batch_id: str, case_key: str, payload: dict) -> None: ...

    def delete_cases_by_batch(self, batch_id: str) -> int: ...


class BatchArchiveStore(Protocol):
    """Almacena el archivo comprimido o fuente original asociado al lote."""

    def save_archive(self, batch_id: str, filename: str, data: bytes) -> str: ...


class BatchWorkingFileStore(Protocol):
    """Guarda archivos temporales o derivados usados durante el procesamiento."""

    def save_file(self, batch_id: str, filename: str, data: bytes) -> str: ...


class ArchiveExtractor(Protocol):
    """Expande un archivo contenedor y devuelve los artefactos descubiertos."""

    def extract_archive(self, archive_path: str, batch_id: str) -> ArchiveExtractionResult: ...


class BatchArtifactCleaner(Protocol):
    """Elimina artefactos temporales creados durante la ingesta batch."""

    def delete_archive(self, archive_path: str) -> None: ...

    def delete_extracted_file(self, file_path: str) -> None: ...

    def delete_batch_artifacts(self, batch_id: str) -> None: ...


class BatchReportStore(Protocol):
    """Persiste reportes generados a partir del lote, por ejemplo Excel."""

    def save_excel_report(self, batch_id: str, filename: str, data: bytes) -> str: ...


class TextExtractor(Protocol):
    """Extrae texto plano desde un archivo clínico procesable."""

    def extract_text(self, file_path: str) -> str: ...

    def extract(self, file_path: str) -> PdfExtractionResult: ...


class PrefacturaPdfExtractor(Protocol):
    """Segmenta PDFs compuestos de prefactura y materializa rangos de páginas."""

    def extract_pages(self, contents: bytes) -> list[PrefacturaPdfPage]: ...

    def build_range_pdf(self, contents: bytes, page_start: int, page_end: int) -> bytes: ...

    def build_pages_pdf(self, contents: bytes, page_numbers: list[int]) -> bytes: ...


class DocumentClassifier(Protocol):
    """Clasifica documentos y puede exponer un título documental estable."""

    def describe(self, filename: str, text: str) -> tuple[str, str]: ...

    def classify(self, filename: str, text: str) -> str: ...

    def inspect(self, filename: str, text: str) -> DocumentClassificationDecision: ...


class PrefacturaClassificationAdvisor(Protocol):
    """Resuelve páginas ambiguas usando un modelo Gemini estructurado."""

    def classify_page(
        self,
        *,
        filename: str,
        page_number: int,
        page_text: str,
        detected_type: str,
        classifier_title: str,
        candidate_types: list[str],
    ) -> PrefacturaPageClassification | None: ...


class CaseAssociationService(Protocol):
    """Extrae señales y agrupa documentos alrededor de un caso clínico."""

    def extract_signals(self, filename: str, text: str, detected_type: str) -> ExtractedSignals: ...

    def associate(self, file_records: list[dict]) -> AssociationResult: ...


class BatchJobDispatcher(Protocol):
    """Despacha el procesamiento asincrónico o en segundo plano de un lote."""

    def dispatch(self, batch_id: str) -> None: ...
