"""Abstract base class for logging backends."""

import asyncio
import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any


@dataclass
class LogRecord:
    """Structured log record for unified logging."""

    timestamp: datetime
    level: int
    message: str
    logger_name: str
    extra: dict[str, Any] = None
    exc_info: tuple[type, Exception, Any] | None = None
    stack_info: str | None = None

    def __post_init__(self):
        """Initialize default values after dataclass creation."""
        if self.extra is None:
            self.extra = {}

    @classmethod
    def from_logging_record(cls, record: logging.LogRecord) -> "LogRecord":
        """Create LogRecord from standard logging.LogRecord.
        
        Args:
            record: Standard Python logging record
            
        Returns:
            Converted LogRecord instance
        """
        extra = {
            k: v for k, v in record.__dict__.items()
            if k not in {
                'name', 'msg', 'args', 'levelname', 'levelno', 'pathname',
                'filename', 'module', 'lineno', 'funcName', 'created',
                'msecs', 'relativeCreated', 'thread', 'threadName',
                'processName', 'process', 'message', 'exc_info', 'exc_text',
                'stack_info'
            }
        }

        return cls(
            timestamp=datetime.fromtimestamp(record.created, tz=timezone.utc),
            level=record.levelno,
            message=record.getMessage(),
            logger_name=record.name,
            extra=extra,
            exc_info=record.exc_info,
            stack_info=record.stack_info
        )


@dataclass
class BackendConfig:
    """Base configuration for logging backends."""

    enabled: bool = True
    timeout: float = 5.0
    retry_attempts: int = 3
    retry_delay: float = 1.0
    
    @classmethod
    def from_config(cls, config: dict[str, Any]) -> "BackendConfig":
        """Create configuration from dictionary.
        
        Args:
            config: Configuration dictionary
            
        Returns:
            BackendConfig instance
        """
        # Filter to only include fields that exist in the dataclass
        fields = {f.name for f in cls.__dataclass_fields__.values()}
        filtered_config = {k: v for k, v in config.items() if k in fields}
        return cls(**filtered_config)


class LoggingBackend(ABC):
    """Abstract base class for logging backends."""

    def __init__(
        self,
        name: str,
        config: BackendConfig,
        **kwargs: Any
    ) -> None:
        """Initialize the logging backend.
        
        Args:
            name: Name of the backend
            config: Backend configuration
            **kwargs: Additional backend-specific parameters
        """
        self.name = name
        self.config = config
        self._connected = False
        self._lock = asyncio.Lock()

    @property
    def is_connected(self) -> bool:
        """Check if backend is connected."""
        return self._connected

    @abstractmethod
    async def connect(self) -> None:
        """Establish connection to the backend."""
        pass

    @abstractmethod
    async def disconnect(self) -> None:
        """Close connection to the backend."""
        pass

    @abstractmethod
    async def send_log(self, record: LogRecord) -> bool:
        """Send a log record to the backend.
        
        Args:
            record: The log record to send
            
        Returns:
            True if successfully sent, False otherwise
        """
        pass

    async def send_logs_batch(self, records: list[LogRecord]) -> list[bool]:
        """Send multiple log records as a batch.
        
        Args:
            records: List of log records to send
            
        Returns:
            List of success/failure status for each record
        """
        # Default implementation sends one by one
        # Backends can override for true batch sending
        results = []
        for record in records:
            try:
                success = await self.send_log(record)
                results.append(success)
            except Exception:
                results.append(False)
        return results

    @abstractmethod
    async def health_check(self) -> bool:
        """Check if backend is healthy and responsive.
        
        Returns:
            True if healthy, False otherwise
        """
        pass

    async def _retry_operation(
        self,
        operation: Any,
        *args: Any,
        **kwargs: Any
    ) -> Any:
        """Retry an operation with exponential backoff.
        
        Args:
            operation: The async operation to retry
            *args: Operation arguments
            **kwargs: Operation keyword arguments
            
        Returns:
            Operation result
            
        Raises:
            Exception: If all retry attempts fail
        """
        last_exception = None

        for attempt in range(self.config.retry_attempts + 1):
            try:
                return await operation(*args, **kwargs)
            except Exception as e:
                last_exception = e
                if attempt < self.config.retry_attempts:
                    delay = self.config.retry_delay * (2 ** attempt)
                    await asyncio.sleep(delay)
                    continue
                raise

        if last_exception:
            raise last_exception

    async def __aenter__(self) -> "LoggingBackend":
        """Async context manager entry."""
        await self.connect()
        return self

    async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        """Async context manager exit."""
        await self.disconnect()
