"""Middleware to require credentials (API key and/or MetaInfo)."""

from fastapi import Request

from hezor_common.transfer.base_sdk.meta_info import MetaInfo
from hezor_common.transfer.middlewares.constants import ERR_RES_HEADER_BOTH


class RequireCredentials:
    """Dependency factory for requiring both API key and MetaInfo with configurable realm.

    This class can be instantiated with a custom realm parameter or used as
    a default instance for backward compatibility.

    Parameters
    ----------
    realm : str, optional
        The authentication realm for WWW-Authenticate header
        (default: "Access to the protected resource")

    Examples
    --------
    >>> from fastapi import Depends
    >>> # Using default instance (backward compatible)
    >>> @app.get("/flexible")
    >>> async def flexible_route(cred: tuple = Depends(require_credentials)):
    ...     api_key, meta_info = cred
    ...     if api_key:
    ...         return {"credential_type": "api_key"}
    ...     return {"credential_type": "meta_info", "subject": meta_info.subject}
    >>> # Using custom realm
    >>> @app.get("/protected")
    >>> async def protected_route(cred: tuple = Depends(RequireCredentials("Protected area"))):
    ...     api_key, meta_info = cred
    ...     return {"authenticated": True}
    """

    def __init__(self, realm: str = "Access to the protected resource"):
        """Initialize the credentials dependency with custom realm.

        Parameters
        ----------
        realm : str, optional
            The authentication realm for WWW-Authenticate header
        """
        self.realm = realm

    def __call__(self, request: Request) -> tuple[str | None, MetaInfo | None]:
        """Dependency to extract and require both valid credentials.

        Parameters
        ----------
        request : Request
            FastAPI request object

        Returns
        -------
        tuple[str | None, MetaInfo | None]
            Tuple of (api_key, meta_info) where both are not None

        Raises
        ------
        HTTPException
            If both credentials are missing or invalid
        """
        from fastapi import HTTPException

        api_key = request.state.api_key if hasattr(request.state, "api_key") else None
        meta_info = request.state.meta_info if hasattr(request.state, "meta_info") else None

        # Check if both authentication methods succeeded
        if api_key is None or meta_info is None:
            # Collect all errors
            errors = []
            if hasattr(request.state, "api_key_error") and request.state.api_key_error:
                errors.append(f"API key: {request.state.api_key_error}")
            if hasattr(request.state, "meta_info_error") and request.state.meta_info_error:
                errors.append(f"MetaInfo: {request.state.meta_info_error}")

            if errors:
                detail = "Credential validation failed: " + "; ".join(errors)
            else:
                detail = "Valid credential required (API key and MetaInfo)"

            raise HTTPException(
                status_code=401, detail=detail, headers=ERR_RES_HEADER_BOTH(realm=self.realm)
            )

        return api_key, meta_info


class GetCredentials:
    """Dependency factory for optional credentials extraction.

    This class can be instantiated or used as a default instance for backward compatibility.

    Examples
    --------
    >>> from fastapi import Depends
    >>> # Using default instance (backward compatible)
    >>> @app.get("/flexible")
    >>> async def flexible_route(cred: tuple = Depends(get_credentials)):
    ...     api_key, meta_info = cred
    ...     if api_key or meta_info:
    ...         return {"authenticated": True}
    ...     return {"authenticated": False}
    """

    def __call__(self, request: Request) -> tuple[str | None, MetaInfo | None]:
        """Dependency to optionally extract credentials.

        Parameters
        ----------
        request : Request
            FastAPI request object

        Returns
        -------
        tuple[str | None, MetaInfo | None]
            Tuple of (api_key, meta_info), both may be None
        """
        api_key = request.state.api_key if hasattr(request.state, "api_key") else None
        meta_info = request.state.meta_info if hasattr(request.state, "meta_info") else None
        return api_key, meta_info


# Default instances for backward compatibility
require_credentials = RequireCredentials()
get_credentials = GetCredentials()
