"""Performance monitoring wrapper for anime providers.

This module provides a wrapper that adds automatic performance tracking
to any provider implementation.
"""
import logging
import time
from typing import Any, Callable, Dict, List, Optional

from src.core.providers.base_provider import Loader
from src.core.providers.health_monitor import get_health_monitor

logger = logging.getLogger(__name__)


class MonitoredProviderWrapper(Loader):
    """Wrapper that adds performance monitoring to any provider."""

    def __init__(
        self,
        provider: Loader,
        enable_monitoring: bool = True,
    ):
        """Initialize monitored provider wrapper.

        Args:
            provider: Provider instance to wrap.
            enable_monitoring: Whether to enable performance monitoring.
        """
        self._provider = provider
        self._enable_monitoring = enable_monitoring
        self._health_monitor = (
            get_health_monitor() if enable_monitoring else None
        )

        logger.info(
            f"Monitoring wrapper initialized for provider: "
            f"{provider.get_site_key()}"
        )

    def _record_operation(
        self,
        operation_name: str,
        start_time: float,
        success: bool,
        bytes_transferred: int = 0,
        error_message: Optional[str] = None,
    ) -> None:
        """Record operation metrics.

        Args:
            operation_name: Name of the operation.
            start_time: Operation start time (from time.time()).
            success: Whether operation succeeded.
            bytes_transferred: Number of bytes transferred.
            error_message: Error message if operation failed.
        """
        if not self._enable_monitoring or not self._health_monitor:
            return

        elapsed_ms = (time.time() - start_time) * 1000
        provider_name = self._provider.get_site_key()

        self._health_monitor.record_request(
            provider_name=provider_name,
            success=success,
            response_time_ms=elapsed_ms,
            bytes_transferred=bytes_transferred,
            error_message=error_message,
        )

        if success:
            logger.debug(
                f"{operation_name} succeeded for {provider_name} "
                f"in {elapsed_ms:.2f}ms"
            )
        else:
            logger.warning(
                f"{operation_name} failed for {provider_name} "
                f"in {elapsed_ms:.2f}ms: {error_message}"
            )

    def search(self, word: str) -> List[Dict[str, Any]]:
        """Search for anime series by name (with monitoring).

        Args:
            word: Search term to look for.

        Returns:
            List of found series as dictionaries.
        """
        start_time = time.time()
        try:
            result = self._provider.search(word)
            self._record_operation(
                operation_name="search",
                start_time=start_time,
                success=True,
            )
            return result
        except Exception as e:
            self._record_operation(
                operation_name="search",
                start_time=start_time,
                success=False,
                error_message=str(e),
            )
            raise

    def is_language(
        self,
        season: int,
        episode: int,
        key: str,
        language: str = "German Dub",
    ) -> bool:
        """Check if episode exists in specified language (monitored).

        Args:
            season: Season number (1-indexed).
            episode: Episode number (1-indexed).
            key: Unique series identifier/key.
            language: Language to check (default: German Dub).

        Returns:
            True if episode exists in specified language.
        """
        start_time = time.time()
        try:
            result = self._provider.is_language(
                season, episode, key, language
            )
            self._record_operation(
                operation_name="is_language",
                start_time=start_time,
                success=True,
            )
            return result
        except Exception as e:
            self._record_operation(
                operation_name="is_language",
                start_time=start_time,
                success=False,
                error_message=str(e),
            )
            raise

    def download(
        self,
        base_directory: str,
        serie_folder: str,
        season: int,
        episode: int,
        key: str,
        language: str = "German Dub",
        progress_callback: Optional[Callable[[str, Dict], None]] = None,
    ) -> bool:
        """Download episode to specified directory (with monitoring).

        Args:
            base_directory: Base directory for downloads.
            serie_folder: Series folder name.
            season: Season number.
            episode: Episode number.
            key: Unique series identifier/key.
            language: Language version to download.
            progress_callback: Optional callback for progress updates.

        Returns:
            True if download successful.
        """
        start_time = time.time()
        bytes_transferred = 0

        # Wrap progress callback to track bytes
        if progress_callback and self._enable_monitoring:

            def monitored_callback(event_type: str, data: Dict) -> None:
                nonlocal bytes_transferred
                if event_type == "progress" and "downloaded" in data:
                    bytes_transferred = data.get("downloaded", 0)
                progress_callback(event_type, data)

            wrapped_callback = monitored_callback
        else:
            wrapped_callback = progress_callback

        try:
            result = self._provider.download(
                base_directory=base_directory,
                serie_folder=serie_folder,
                season=season,
                episode=episode,
                key=key,
                language=language,
                progress_callback=wrapped_callback,
            )
            self._record_operation(
                operation_name="download",
                start_time=start_time,
                success=result,
                bytes_transferred=bytes_transferred,
            )
            return result
        except Exception as e:
            self._record_operation(
                operation_name="download",
                start_time=start_time,
                success=False,
                bytes_transferred=bytes_transferred,
                error_message=str(e),
            )
            raise

    def get_site_key(self) -> str:
        """Get the site key/identifier for this provider.

        Returns:
            Site key string.
        """
        return self._provider.get_site_key()

    def get_title(self, key: str) -> str:
        """Get the human-readable title of a series.

        Args:
            key: Unique series identifier/key.

        Returns:
            Series title string.
        """
        start_time = time.time()
        try:
            result = self._provider.get_title(key)
            self._record_operation(
                operation_name="get_title",
                start_time=start_time,
                success=True,
            )
            return result
        except Exception as e:
            self._record_operation(
                operation_name="get_title",
                start_time=start_time,
                success=False,
                error_message=str(e),
            )
            raise

    def get_season_episode_count(self, slug: str) -> Dict[int, int]:
        """Get season and episode counts for a series.

        Args:
            slug: Series slug/key identifier.

        Returns:
            Dictionary mapping season number to episode count.
        """
        start_time = time.time()
        try:
            result = self._provider.get_season_episode_count(slug)
            self._record_operation(
                operation_name="get_season_episode_count",
                start_time=start_time,
                success=True,
            )
            return result
        except Exception as e:
            self._record_operation(
                operation_name="get_season_episode_count",
                start_time=start_time,
                success=False,
                error_message=str(e),
            )
            raise

    @property
    def wrapped_provider(self) -> Loader:
        """Get the underlying provider instance.

        Returns:
            Wrapped provider instance.
        """
        return self._provider


def wrap_provider(
    provider: Loader,
    enable_monitoring: bool = True,
) -> Loader:
    """Wrap a provider with performance monitoring.

    Args:
        provider: Provider to wrap.
        enable_monitoring: Whether to enable monitoring.

    Returns:
        Monitored provider wrapper.
    """
    if isinstance(provider, MonitoredProviderWrapper):
        # Already wrapped
        return provider

    return MonitoredProviderWrapper(
        provider=provider,
        enable_monitoring=enable_monitoring,
    )
