"""Abstract base classes for document processing and retrieval handlers.

Handlers are the core extension point for tenant adapters. Developers
implement concrete subclasses to define their processing and retrieval logic.
"""

from __future__ import annotations

from abc import ABC, abstractmethod

from entr_adapter_core.schemas import (
    AdapterResponse,
    DocumentDownloadResponse,
    DocumentPayload,
    RetrievalPayload,
    RetrievalResponse,
)


class BaseHandler(ABC):
    """Abstract base class for document processing handlers.

    Handlers are instantiated **once at startup** (singleton pattern) and
    shared across all concurrent requests. Use ``self`` only for shared
    resources such as settings, clients, or configuration.

    WARNING: Never store request-specific state on ``self``.
    Concurrent requests share the same handler instance. Storing per-request
    data on ``self`` (e.g., ``self.current_document = ...``) causes subtle
    data-corruption bugs under load. Use local variables in ``process()``
    for all per-request data.
    """

    def __init__(self, settings=None, retrieval_handler: RetrievalHandler | None = None, **kwargs):
        """Initialize the handler with shared resources.

        Args:
            settings: Adapter-specific settings/configuration object.
                Typically a Pydantic settings model or dict. Available as
                ``self.settings`` throughout the handler's lifetime.
            retrieval_handler: Optional retrieval handler instance for
                document listing and downloading. Injected at construction
                and available as ``self.retrieval_handler``.
            **kwargs: Additional adapter-specific arguments. Stored as
                ``self.kwargs`` for flexible extension.
        """
        self.settings = settings
        self.retrieval_handler = retrieval_handler
        self.kwargs = kwargs

    @abstractmethod
    async def process(self, payload: DocumentPayload) -> AdapterResponse:
        """Process a document payload and return an adapter response.

        Recommended flow:
        1. **Validate** — check input payload for required fields and business rules
        2. **Transform** — convert extracted data into target system format
        3. **Send** — deliver transformed data to the target system (ERP, CRM, etc.)
        4. **Respond** — return an ``AdapterResponse`` with the appropriate status

        WARNING: This method is called concurrently for multiple requests.
        Do NOT store request-specific data on ``self``. Use local variables
        for all per-request data.

        Args:
            payload: The document payload sent by ENTR Core.

        Returns:
            An ``AdapterResponse`` indicating success, failure, or review_required.
        """
        ...


class RetrievalHandler(ABC):
    """Abstract base class for document retrieval handlers.

    Retrieval handlers enable adapters to pull documents from external
    sources (email inboxes, FTP servers, APIs, etc.) on behalf of ENTR Core.

    Like ``BaseHandler``, retrieval handlers are singletons. Do not store
    request-specific state on ``self``.
    """

    def __init__(self, settings=None, **kwargs):
        """Initialize the retrieval handler with shared resources.

        Args:
            settings: Adapter-specific settings/configuration object.
                Available as ``self.settings`` throughout the handler's lifetime.
            **kwargs: Additional adapter-specific arguments.
        """
        self.settings = settings
        self.kwargs = kwargs

    @abstractmethod
    async def list_documents(self, payload: RetrievalPayload) -> RetrievalResponse:
        """List available documents from an external source.

        Called by Core's scheduler to discover new documents that should
        be downloaded and processed.

        Args:
            payload: Contains the source identifier and optional date filter.

        Returns:
            A ``RetrievalResponse`` with the list of available documents.
        """
        ...

    @abstractmethod
    async def get_document(self, document_identifier: str) -> DocumentDownloadResponse:
        """Download a specific document by its identifier.

        Called by Core after ``list_documents()`` identifies new documents.

        Args:
            document_identifier: The unique identifier for the document
                (as returned in ``DocumentIdentifier.document_identifier``).

        Returns:
            A ``DocumentDownloadResponse`` with the base64-encoded file content.
        """
        ...
