from __future__ import annotations

from enum import StrEnum
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable


if TYPE_CHECKING:
    from app.llm.models import (
        LLMProviderAvailability,
        LLMProviderCapabilities,
        LLMResolvedRoute,
        LLMStructuredRequest,
        LLMStructuredResult,
        LLMTextRequest,
        LLMTextResult,
    )


class LLMTask(StrEnum):
    HISTORIA_CHUNK_SUMMARY = "historia_chunk_summary"
    HISTORIA_FINAL_SUMMARY = "historia_final_summary"
    CLINICAL_DOCUMENT_EXTRACT = "clinical_document_extract"
    PREFACTURA_PAGE_CLASSIFICATION = "prefactura_page_classification"
    SOAT_REASONING = "soat_reasoning"
    SOAT_CODE_GENERATION = "soat_code_generation"
    SOAT_CHAT = "soat_chat"
    SOAT_MANUAL_CODE_LOOKUP = "soat_manual_code_lookup"
    GLOSA_NOTE_GENERATION = "glosa_note_generation"
    MEDICATION_PERTINENCE = "medication_pertinence"
    CIE10_RESOLUTION = "cie10_resolution"
    CUPS_RESOLUTION = "cups_resolution"


class LLMOutputKind(StrEnum):
    STRUCTURED_OBJECT = "structured_object"
    STRUCTURED_COLLECTION = "structured_collection"
    CONTROLLED_PLAIN_TEXT = "controlled_plain_text"


@runtime_checkable
class StructuredGenerationPort(Protocol):
    def generate_structured(self, request: LLMStructuredRequest) -> LLMStructuredResult: ...


@runtime_checkable
class LLMProviderPort(StructuredGenerationPort, Protocol):
    def provider_name(self) -> str: ...

    def availability(self) -> LLMProviderAvailability: ...

    def capabilities(self) -> LLMProviderCapabilities: ...

    def generate_text(self, request: LLMTextRequest) -> LLMTextResult: ...

    def close(self) -> None: ...


@runtime_checkable
class ModelSelectionPolicy(Protocol):
    def resolve(self, task: LLMTask, metadata: dict[str, Any] | None = None) -> LLMResolvedRoute: ...


@runtime_checkable
class PromptCompressorPort(Protocol):
    def compress(self, prompt: str, *, task: LLMTask | None = None, metadata: dict[str, Any] | None = None) -> str: ...


class NullPromptCompressor:
    def compress(self, prompt: str, *, task: LLMTask | None = None, metadata: dict[str, Any] | None = None) -> str:
        _ = task, metadata
        return prompt
