"""
🚀 EmpowerNow Common Package

The world's most comprehensive identity and security library for Python.
Supporting advanced OAuth 2.0/2.1, AuthZEN, FIPS compliance, and more.

Quick Start:
    # Advanced OAuth with auto-registration
    from empowernow_common import AdvancedOAuth, AuthorizationDetail

    oauth = await AdvancedOAuth.auto_register(
        issuer="https://auth.example.com",
        app_name="My App"
    )

    # FIPS-compliant operations
    from empowernow_common import FIPSAlgorithms

    is_approved = FIPSAlgorithms.is_algorithm_approved("RS256", "jwt_signing")
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

# 🔒 FIPS 140-3 Compliance
#
# IMPORTANT: keep FIPS imports lazy.
# - Importing `cryptography` (PyO3/Rust bindings) at module import-time can break
#   certain tooling (notably coverage/pytest-cov on some Windows environments).
# - This package already documents that apps should call `empowernow_common.init()`
#   once at startup for opt-in behavior; importing this top-level module should
#   not have heavy side effects.
if TYPE_CHECKING:
    from .fips import FIPSAlgorithms as FIPSAlgorithms  # noqa: F401
    from .oauth.client import AdvancedToken, OAuthConfig, HardenedOAuth, OAuth, Token  # noqa: F401
    from .oauth.ciba import CIBARequest  # noqa: F401
    from .oauth.rar import SecureAuthorizationDetail as AuthorizationDetail  # noqa: F401
    from .oauth.security import generate_correlation_id  # noqa: F401
    from .oauth.sync import SyncOAuth  # noqa: F401
    from .authzen import (  # noqa: F401
        Subject,
        Who,
        Resource,
        What,
        Action,
        How,
        Context,
        When,
        AuthRequest,
        AuthResponse,
        PDP,
        PDPClient,
        PolicyClient,
        AuthzClient,
        PDPConfig,
        AuthResult,
    )
    from .exceptions import EmpowerNowError, OAuthError  # noqa: F401
    from .identity import UniqueIdentity  # noqa: F401
    from .jwt import IdPConfig, IdPCatalogue, peek_header, peek_payload  # noqa: F401
    from .oauth.claims import ClaimsMapper  # noqa: F401
    from .utils.logging_config import EmojiDowngradeFilter, setup_default_logging  # noqa: F401


def __getattr__(name: str) -> Any:
    if name == "FIPSAlgorithms":
        from .fips import FIPSAlgorithms as _FIPSAlgorithms

        return _FIPSAlgorithms
    if name in {"AdvancedToken", "OAuthConfig", "HardenedOAuth", "OAuth", "Token"}:
        from .oauth.client import (  # import lazily to avoid heavy crypto at import-time
            AdvancedToken as _AdvancedToken,
            OAuthConfig as _OAuthConfig,
            HardenedOAuth as _HardenedOAuth,
            OAuth as _OAuth,
            Token as _Token,
        )

        return {
            "AdvancedToken": _AdvancedToken,
            "OAuthConfig": _OAuthConfig,
            "HardenedOAuth": _HardenedOAuth,
            "OAuth": _OAuth,
            "Token": _Token,
        }[name]
    if name == "SyncOAuth":
        from .oauth.sync import SyncOAuth as _SyncOAuth

        return _SyncOAuth
    if name == "generate_correlation_id":
        from .oauth.security import generate_correlation_id as _generate_correlation_id

        return _generate_correlation_id
    if name == "AuthorizationDetail":
        from .oauth.rar import SecureAuthorizationDetail as _AuthorizationDetail

        return _AuthorizationDetail
    if name == "CIBARequest":
        from .oauth.ciba import CIBARequest as _CIBARequest

        return _CIBARequest
    if name in {
        "Subject",
        "Who",
        "Resource",
        "What",
        "Action",
        "How",
        "Context",
        "When",
        "AuthRequest",
        "AuthResponse",
        "PDP",
        "PDPClient",
        "PolicyClient",
        "AuthzClient",
        "PDPConfig",
        "AuthResult",
    }:
        from . import authzen as _authzen

        return getattr(_authzen, name)
    if name in {"setup_default_logging", "EmojiDowngradeFilter"}:
        from .utils.logging_config import (
            EmojiDowngradeFilter as _EmojiDowngradeFilter,
            setup_default_logging as _setup_default_logging,
        )

        return {
            "setup_default_logging": _setup_default_logging,
            "EmojiDowngradeFilter": _EmojiDowngradeFilter,
        }[name]
    if name in {"peek_header", "peek_payload", "IdPConfig", "IdPCatalogue"}:
        from .jwt import (
            IdPConfig as _IdPConfig,
            IdPCatalogue as _IdPCatalogue,
            peek_header as _peek_header,
            peek_payload as _peek_payload,
        )

        return {
            "peek_header": _peek_header,
            "peek_payload": _peek_payload,
            "IdPConfig": _IdPConfig,
            "IdPCatalogue": _IdPCatalogue,
        }[name]
    if name == "ClaimsMapper":
        from .oauth.claims import ClaimsMapper as _ClaimsMapper

        return _ClaimsMapper
    if name == "UniqueIdentity":
        from .identity import UniqueIdentity as _UniqueIdentity

        return _UniqueIdentity
    if name in {"EmpowerNowError", "OAuthError"}:
        from .exceptions import EmpowerNowError as _EmpowerNowError, OAuthError as _OAuthError

        return {"EmpowerNowError": _EmpowerNowError, "OAuthError": _OAuthError}[name]
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

# -------------------------------------------------------------
# Public initialization helper – kill hidden side-effects 🧹
# -------------------------------------------------------------
# Applications MUST call empowernow_common.init() once at startup
# to enable FIPS checks, default logging, and other opt-in helpers.
# This avoids network / thread side-effects at import-time.

import warnings as _warnings
import logging as _logging

from .settings import EmpowerNowSettings, settings as _global_settings


def init(
    *,
    strict_fips: bool = False,
    enable_default_logging: bool = True,
    settings: EmpowerNowSettings | None = None,
) -> None:
    """Initialize EmpowerNow Common helpers.

    Args:
        strict_fips:  Fail hard if FIPS validation fails (default: False – warn only).
        enable_default_logging:  Configure root logger with sane defaults unless the
            host application already did so (default: True).
        settings:  Optional EmpowerNowSettings object to override global settings.
    """

    # 0) Override global settings early
    if settings is not None:
        globals()["_global_settings"] = settings  # type: ignore[misc]

    # 1) Default logging (uses settings.log_json_default inside)
    if enable_default_logging:
        # Lazy import avoids importing oauth/cryptography at module import-time.
        from .utils.logging_config import setup_default_logging as _setup_default_logging

        _setup_default_logging()

    # 2) FIPS validation (can be long-running / spawn background task)
    from .fips import FIPSValidator as _FIPSValidator

    try:
        _FIPSValidator.ensure_compliance()

        # Opt-in continuous validation via env vars – keep behaviour unchanged.
        import os

        if os.getenv("EMPOWERNOW_FIPS_CONTINUOUS", "false").lower() in {
            "true",
            "1",
            "yes",
        }:
            from .fips.validator import start_continuous_validation as _start_cont

            interval = int(os.getenv("EMPOWERNOW_FIPS_INTERVAL", "300"))
            _start_cont(interval, strict_fips)

    except RuntimeError as exc:  # pragma: no cover – environment-specific
        if strict_fips:
            raise
        _warnings.warn(str(exc), RuntimeWarning)
        _logging.getLogger(__name__).warning(
            "FIPS validation failed – continuing in non-FIPS mode"
        )


# Explicit version for PyPI release – keep in sync with pyproject.toml
__version__ = "3.4.9"


__all__ = [
    # 🔒 FIPS
    "FIPSAlgorithms",
    "generate_correlation_id",
    # 🚀 OAuth façade
    "AdvancedToken",
    "OAuthConfig",
    # 🎯 AuthZEN façade
    "Subject",
    "Who",
    "Resource",
    "What",
    "Action",
    "How",
    "Context",
    "When",
    "AuthRequest",
    "AuthResponse",
    "PDP",
    "PDPClient",
    "PolicyClient",
    "AuthzClient",
    "PDPConfig",
    "AuthResult",
    # Exceptions
    "EmpowerNowError",
    "OAuthError",
    # Initialiser
    "init",
    # IdP catalogue
    "IdPConfig",
    "IdPCatalogue",
    "UniqueIdentity",
    # JWT helpers
    "peek_header",
    "peek_payload",
    "ClaimsMapper",
    "SyncOAuth",
    # Factory helpers
    "async_oauth",
    "sync_oauth",
    "secure_token",
]

# ------------------------------------------------------------------
# Lightweight factory helpers – keep import-time cost near-zero
# ------------------------------------------------------------------
# No heavy imports at module load – only stdlib / lightweight types
from typing import Any, Dict
from .oauth.client import SecureOAuthConfig, HardenedOAuth, HardenedToken
from contextlib import asynccontextmanager


def _to_config(cfg: SecureOAuthConfig | Dict[str, Any]) -> SecureOAuthConfig:
    if isinstance(cfg, SecureOAuthConfig):
        return cfg
    return SecureOAuthConfig(**cfg)  # type: ignore[arg-type]


@asynccontextmanager
async def async_oauth(**kwargs):  # noqa: D401 – factory returns context manager
    """Async factory yielding a ready-to-use :class:`HardenedOAuth`.

    Always use via::

        async with async_oauth(**cfg) as oauth:
            ...

    This pattern prevents connection-pool leaks if an exception bubbles and
    mirrors best-practice for `httpx.AsyncClient`.
    """

    client = HardenedOAuth(_to_config(kwargs))  # type: ignore[arg-type]
    try:
        yield client
    finally:
        await client.aclose()


def sync_oauth(**kwargs) -> "SyncOAuth":  # noqa: D401 – factory name is verbish
    """Return a **sync** :class:`~empowernow_common.oauth.sync.SyncOAuth` instance."""

    from .oauth.sync import SyncOAuth  # local import to avoid heavy deps at import time

    cfg = _to_config(kwargs)  # type: ignore[arg-type]
    return SyncOAuth(cfg)


def secure_token(**kwargs) -> HardenedToken:  # noqa: D401 – tiny convenience
    """Blocking helper to fetch a single access-token in **one line**.

    The call wraps ::

        SyncOAuth(**kwargs).get_token()

    so callers can replace verbose boilerplate when they just need a quick
    token in scripts or CI jobs.
    """

    from .oauth.sync import SyncOAuth  # local import to keep startup fast

    return SyncOAuth(_to_config(kwargs)).get_token()


# -------------------------------------------------------------
# ⚠️  Deprecated import-time side-effects removed. Users must now
#      call `empowernow_common.init()` explicitly. 2024-Q2 release.
# -------------------------------------------------------------

# Re-export cache backends and ARN helpers so downstream code can do
# ``from empowernow_common import RedisCacheBackend`` without deep paths.

from .cache import CacheBackend, InMemoryCacheBackend, RedisCacheBackend  # noqa: F401

from .arn import (
    parse as parse_arn,
    validate as validate_arn,
    is_user as is_user_arn,
    to_user_id,
)

# Audience constants (generated from IdP registry)
from .aud import (
    AUD_CRUD,
    AUD_MEMBERSHIP,
    AUD_PDP,
    AUD_ANALYTICS,
    AUD_DATACOLLECTOR,
    AUD_IDP_ADMIN,
    ALL_AUDIENCES,
    AUDIENCE_NAMES,
)
from .jwt.lightweight_validator import (  # noqa: F401
    LightweightValidator as LightweightJWTValidator,
    ValidationError as LightweightJWTValidationError,
    create_validator as create_lightweight_jwt_validator,
)

from .secret_loader import load_secret, register_provider, SecretLoaderError, SecretNotFound  # noqa: E401,F401

# 🔐 Delegation Module v2.3
from .delegation import (
    # Protocol versioning
    DELEGATION_PROTOCOL_VERSION,
    SUPPORTED_VERSIONS as DELEGATION_SUPPORTED_VERSIONS,
    # Enums
    DelegationStatus,
    TrustLevel,
    PreCheckResult,
    EnforceDecision,
    VerificationSource,
    # Models
    Delegation,
    EnforceResult,
    # Verifier
    DelegationVerifier,
    # Capability
    capability_allowed,
    capability_allowed_with_match,
    # Cache
    DelegationCache,
    DelegationCacheConfig,
    InMemoryDelegationCache,
    RedisDelegationCache,
    # Exceptions
    DelegationError,
    DelegationNotFoundError,
    DelegationExpiredError,
    DelegationRevokedError,
    DelegationSuspendedError,
    CapabilityNotAllowedError,
    ProtocolVersionError,
    DelegationVerificationError,
    # Events
    DelegationEvent,
    DelegationEventType,
    DelegationEventHandler,
    create_event_handler,
)

# 🔧 MCP Tool ID Module
from .mcp import (
    ToolId,
    parse_tool_id,
    is_valid_tool_id,
    is_loopback_server,
    DEFAULT_LOOPBACK_SERVERS,
    ServerConfig,
    ServerRegistry,
)

# 🔑 Auth Module (Loopback Signing)
from .auth import (
    LoopbackSigner,
    LoopbackVerifier,
    CanonicalRequest,
    VerificationResult,
    HEADER_SIGNATURE,
    HEADER_TIMESTAMP,
    HEADER_NONCE,
    HEADER_AUDIENCE,
)

# 🚫 Denial Response Module (GAP-016)
from .denial import (
    DenialReason,
    DenialResponse,
    BudgetExceededData,
    RateLimitedData,
    DelegationRequiredData,
    TimeRestrictedData,
    ModelRestrictedData,
    ContentBlockedData,
    CapabilityNotAllowedData,
    CandidateLimitExceededData,
)

__all__ = [
    # cache
    "CacheBackend",
    "InMemoryCacheBackend",
    "RedisCacheBackend",
    # arn
    "parse_arn",
    "validate_arn",
    "is_user_arn",
    "to_user_id",
    # audience constants (generated)
    "AUD_CRUD",
    "AUD_MEMBERSHIP",
    "AUD_PDP",
    "AUD_ANALYTICS",
    "AUD_DATACOLLECTOR",
    "AUD_IDP_ADMIN",
    "ALL_AUDIENCES",
    "AUDIENCE_NAMES",
    # lightweight jwt validator
    "LightweightJWTValidator",
    "LightweightJWTValidationError",
    "create_lightweight_jwt_validator",
    # 🔐 Delegation Module v2.3
    "DELEGATION_PROTOCOL_VERSION",
    "DELEGATION_SUPPORTED_VERSIONS",
    "DelegationStatus",
    "TrustLevel",
    "PreCheckResult",
    "EnforceDecision",
    "VerificationSource",
    "Delegation",
    "EnforceResult",
    "DelegationVerifier",
    "capability_allowed",
    "capability_allowed_with_match",
    "DelegationCache",
    "DelegationCacheConfig",
    "InMemoryDelegationCache",
    "RedisDelegationCache",
    "DelegationError",
    "DelegationNotFoundError",
    "DelegationExpiredError",
    "DelegationRevokedError",
    "DelegationSuspendedError",
    "CapabilityNotAllowedError",
    "ProtocolVersionError",
    "DelegationVerificationError",
    "DelegationEvent",
    "DelegationEventType",
    "DelegationEventHandler",
    "create_event_handler",
    # 🔧 MCP Tool ID Module
    "ToolId",
    "parse_tool_id",
    "is_valid_tool_id",
    "is_loopback_server",
    "DEFAULT_LOOPBACK_SERVERS",
    "ServerConfig",
    "ServerRegistry",
    # 🔑 Auth Module (Loopback Signing)
    "LoopbackSigner",
    "LoopbackVerifier",
    "CanonicalRequest",
    "VerificationResult",
    "HEADER_SIGNATURE",
    "HEADER_TIMESTAMP",
    "HEADER_NONCE",
    "HEADER_AUDIENCE",
    # 🚫 Denial Response Module (GAP-016)
    "DenialReason",
    "DenialResponse",
    "BudgetExceededData",
    "RateLimitedData",
    "DelegationRequiredData",
    "TimeRestrictedData",
    "ModelRestrictedData",
    "ContentBlockedData",
    "CapabilityNotAllowedData",
    "CandidateLimitExceededData",
]
