"""
Audit Logger - Audit logging library for microservices.

Usage:
    from audit_logger import init_audit_logger, audit_logger, set_audit_context

    # Initialize once at app startup
    init_audit_logger(mongo_url="mongodb://localhost:27017", db_name="my_audit_logs")

    # In middleware - set context for each request
    set_audit_context(org_id="org_123", user_id="user_456", ip_address="1.2.3.4")

    # In your code - log audit events
    audit_logger.log_create(AuditResource.AGENT, agent_id="agent_789", resource_name="My Agent")
"""

import os
import threading
from typing import Optional

from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorCollection
import redis.asyncio as aioredis

# Import all public types
from .enums import (
    AuditAction,
    AuditResource,
    AuditResult,
    AuditSeverity,
    LogLevel,
    LogStatus,
)
from .models import (
    AuditActor,
    AuditTarget,
    AuditChange,
    AuditLogEntry,
    AuditLogQuery,
    GuardrailViolation,
)
from .manager import AuditLogManager
from .context import (
    AuditLogger,
    set_audit_context,
    get_audit_context,
    clear_audit_context,
    audit_context,
    compute_changes,
    init_audit_context,
)

# Module-level singleton instances (initialized lazily)
_init_lock = threading.Lock()
_audit_log_manager: Optional[AuditLogManager] = None
_audit_logger: Optional[AuditLogger] = None
_mongo_client: Optional[AsyncIOMotorClient] = None
_redis_client: Optional[aioredis.Redis] = None


def init_audit_logger(
    mongo_url: Optional[str] = None,
    db_name: str = "lyzr_audit_logs",
    collection_name: str = "audit_logs",
    activity_collection_name: str = "user_activity",
    collection: Optional[AsyncIOMotorCollection] = None,
    activity_collection: Optional[AsyncIOMotorCollection] = None,
    redis_url: Optional[str] = None,
) -> tuple[AuditLogManager, AuditLogger]:
    """
    Initialize the audit logger with MongoDB and Redis connections.

    Args:
        mongo_url: MongoDB connection URL. Defaults to AUDIT_MONGO_URL env var
                   or "mongodb://localhost:27017"
        db_name: Database name for audit logs
        collection_name: Collection name for audit logs
        collection: Optional pre-configured AsyncIOMotorCollection to use instead
        activity_collection: Optional pre-configured AsyncIOMotorCollection for user activity tracking
        redis_url: Redis connection URL. Defaults to AUDIT_REDIS_URL env var
                   or "redis://localhost:6379"
    Returns:
        Tuple of (AuditLogManager, AuditLogger) instances

    Example:
        # Option 1: Auto-configure from environment
        manager, logger = init_audit_logger()

        # Option 2: Explicit configuration
        manager, logger = init_audit_logger(
            mongo_url="mongodb://audit-db:27017",
            db_name="my_service_audit",
            redis_url="redis://localhost:6379"
        )

        # Option 3: Use existing collection
        manager, logger = init_audit_logger(collection=my_collection)
    """
    global _audit_log_manager, _audit_logger, _mongo_client, _redis_client

    with _init_lock:
        # Initialize Redis client
        _redis_client = aioredis.Redis(
            host=redis_url,
            port=6379,
            db=0,
            encoding="utf-8",
            decode_responses=True,
            max_connections=50,  # Connection pool limit
            socket_connect_timeout=5,  # 5 second connection timeout
            socket_timeout=5,  # 5 second operation timeout
        )

        if collection is not None:
            # Use provided collection
            _audit_log_manager = AuditLogManager(
                collection=collection,
                activity_collection=activity_collection,
                redis_client=_redis_client,
            )
        else:
            # Create new connection
            url = mongo_url or os.getenv("AUDIT_MONGO_URL", "mongodb://localhost:27017")
            _mongo_client = AsyncIOMotorClient(
                url,
                maxPoolSize=10,
            )
            db = _mongo_client[db_name]
            _audit_log_manager = AuditLogManager(
                collection=db[collection_name],
                activity_collection=db[activity_collection_name],
                redis_client=_redis_client,
            )

        _audit_logger = AuditLogger(_audit_log_manager)

        # Initialize context module for automatic auth logging
        init_audit_context(_audit_log_manager)

        return _audit_log_manager, _audit_logger


def get_audit_log_manager() -> AuditLogManager:
    """
    Get the initialized AuditLogManager instance.
    Raises RuntimeError if init_audit_logger() hasn't been called.
    """
    if _audit_log_manager is None:
        raise RuntimeError(
            "Audit logger not initialized. Call init_audit_logger() first."
        )
    return _audit_log_manager


def get_audit_logger() -> AuditLogger:
    """
    Get the initialized AuditLogger instance.
    Raises RuntimeError if init_audit_logger() hasn't been called.
    """
    if _audit_logger is None:
        raise RuntimeError(
            "Audit logger not initialized. Call init_audit_logger() first."
        )
    return _audit_logger


# Lazy-loading properties for backward compatibility
class _LazyAuditLogManager:
    """Proxy that lazily initializes the audit log manager on first access."""

    def __getattr__(self, name):
        return getattr(get_audit_log_manager(), name)


class _LazyAuditLogger:
    """Proxy that lazily initializes the audit logger on first access."""

    def __getattr__(self, name):
        return getattr(get_audit_logger(), name)


# These can be imported directly but will raise if not initialized
audit_log_manager = _LazyAuditLogManager()
audit_logger = _LazyAuditLogger()


async def shutdown_audit_logger() -> None:
    """
    Gracefully shutdown the audit logger.
    Call this on application shutdown to flush pending writes.
    """
    global _audit_log_manager, _audit_logger, _mongo_client, _redis_client
    with _init_lock:
        if _audit_log_manager is not None:
            await _audit_log_manager.shutdown()
        if _redis_client is not None:
            await _redis_client.close()
        if _mongo_client is not None:
            _mongo_client.close()
        _audit_log_manager = None
        _audit_logger = None
        _mongo_client = None
        _redis_client = None


__all__ = [
    # Initialization
    "init_audit_logger",
    "shutdown_audit_logger",
    "get_audit_log_manager",
    "get_audit_logger",
    # Singleton instances
    "audit_log_manager",
    "audit_logger",
    # Context management
    "init_audit_context",
    "set_audit_context",
    "get_audit_context",
    "clear_audit_context",
    "audit_context",
    # Decorators and helpers
    "compute_changes",
    # Enums
    "AuditAction",
    "AuditResource",
    "AuditResult",
    "AuditSeverity",
    "LogLevel",
    "LogStatus",
    # Models
    "AuditActor",
    "AuditTarget",
    "AuditChange",
    "AuditLogEntry",
    "AuditLogQuery",
    "GuardrailViolation",
    # Classes
    "AuditLogManager",
    "AuditLogger",
]

__version__ = "0.1.0"
