"""
Progress callback interfaces for core operations.

This module defines clean interfaces for progress reporting, error handling,
and completion notifications across all core operations (scanning,
downloading).
"""

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, Optional

logger = logging.getLogger(__name__)


class OperationType(str, Enum):
    """Types of operations that can report progress."""
    
    SCAN = "scan"
    DOWNLOAD = "download"
    SEARCH = "search"
    INITIALIZATION = "initialization"


class ProgressPhase(str, Enum):
    """Phases of an operation's lifecycle."""
    
    STARTING = "starting"
    IN_PROGRESS = "in_progress"
    COMPLETING = "completing"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


@dataclass
class ProgressContext:
    """
    Complete context information for a progress update.
    
    Attributes:
        operation_type: Type of operation being performed
        operation_id: Unique identifier for this operation
        phase: Current phase of the operation
        current: Current progress value (e.g., files processed)
        total: Total progress value (e.g., total files)
        percentage: Completion percentage (0.0 to 100.0)
        message: Human-readable progress message
        details: Additional context-specific details
        key: Provider-assigned series identifier (None when not applicable)
        folder: Optional folder metadata for display purposes only
        metadata: Extra metadata for specialized use cases
    """
    
    operation_type: OperationType
    operation_id: str
    phase: ProgressPhase
    current: int
    total: int
    percentage: float
    message: str
    details: Optional[str] = None
    key: Optional[str] = None
    folder: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "operation_type": self.operation_type.value,
            "operation_id": self.operation_id,
            "phase": self.phase.value,
            "current": self.current,
            "total": self.total,
            "percentage": round(self.percentage, 2),
            "message": self.message,
            "details": self.details,
            "key": self.key,
            "folder": self.folder,
            "metadata": self.metadata,
        }


@dataclass
class ErrorContext:
    """
    Context information for error callbacks.
    
    Attributes:
        operation_type: Type of operation that failed
        operation_id: Unique identifier for the operation
        error: The exception that occurred
        message: Human-readable error message
        recoverable: Whether the error is recoverable
        retry_count: Number of retry attempts made
        key: Provider-assigned series identifier (None when not applicable)
        folder: Optional folder metadata for display purposes only
        metadata: Additional error context
    """
    
    operation_type: OperationType
    operation_id: str
    error: Exception
    message: str
    recoverable: bool = False
    retry_count: int = 0
    key: Optional[str] = None
    folder: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "operation_type": self.operation_type.value,
            "operation_id": self.operation_id,
            "error_type": type(self.error).__name__,
            "error_message": str(self.error),
            "message": self.message,
            "recoverable": self.recoverable,
            "retry_count": self.retry_count,
            "key": self.key,
            "folder": self.folder,
            "metadata": self.metadata,
        }


@dataclass
class CompletionContext:
    """
    Context information for completion callbacks.
    
    Attributes:
        operation_type: Type of operation that completed
        operation_id: Unique identifier for the operation
        success: Whether the operation completed successfully
        message: Human-readable completion message
        result_data: Result data from the operation
        statistics: Operation statistics (duration, items processed, etc.)
        key: Provider-assigned series identifier (None when not applicable)
        folder: Optional folder metadata for display purposes only
        metadata: Additional completion context
    """
    
    operation_type: OperationType
    operation_id: str
    success: bool
    message: str
    result_data: Optional[Any] = None
    statistics: Dict[str, Any] = field(default_factory=dict)
    key: Optional[str] = None
    folder: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "operation_type": self.operation_type.value,
            "operation_id": self.operation_id,
            "success": self.success,
            "message": self.message,
            "statistics": self.statistics,
            "key": self.key,
            "folder": self.folder,
            "metadata": self.metadata,
        }


class ProgressCallback(ABC):
    """
    Abstract base class for progress callbacks.
    
    Implement this interface to receive progress updates from core operations.
    """
    
    @abstractmethod
    def on_progress(self, context: ProgressContext) -> None:
        """
        Called when progress is made in an operation.
        
        Args:
            context: Complete progress context information
        """
        pass


class ErrorCallback(ABC):
    """
    Abstract base class for error callbacks.
    
    Implement this interface to receive error notifications from core
    operations.
    """
    
    @abstractmethod
    def on_error(self, context: ErrorContext) -> None:
        """
        Called when an error occurs during an operation.
        
        Args:
            context: Complete error context information
        """
        pass


class CompletionCallback(ABC):
    """
    Abstract base class for completion callbacks.
    
    Implement this interface to receive completion notifications from
    core operations.
    """
    
    @abstractmethod
    def on_completion(self, context: CompletionContext) -> None:
        """
        Called when an operation completes (successfully or not).
        
        Args:
            context: Complete completion context information
        """
        pass


class CallbackManager:
    """
    Manages multiple callbacks for an operation.
    
    This class allows registering multiple progress, error, and completion
    callbacks and dispatching events to all registered callbacks.
    """
    
    def __init__(self):
        """Initialize the callback manager."""
        self._progress_callbacks: list[ProgressCallback] = []
        self._error_callbacks: list[ErrorCallback] = []
        self._completion_callbacks: list[CompletionCallback] = []
    
    def register_progress_callback(self, callback: ProgressCallback) -> None:
        """
        Register a progress callback.
        
        Args:
            callback: Progress callback to register
        """
        if callback not in self._progress_callbacks:
            self._progress_callbacks.append(callback)
    
    def register_error_callback(self, callback: ErrorCallback) -> None:
        """
        Register an error callback.
        
        Args:
            callback: Error callback to register
        """
        if callback not in self._error_callbacks:
            self._error_callbacks.append(callback)
    
    def register_completion_callback(
        self,
        callback: CompletionCallback
    ) -> None:
        """
        Register a completion callback.
        
        Args:
            callback: Completion callback to register
        """
        if callback not in self._completion_callbacks:
            self._completion_callbacks.append(callback)
    
    def unregister_progress_callback(self, callback: ProgressCallback) -> None:
        """
        Unregister a progress callback.
        
        Args:
            callback: Progress callback to unregister
        """
        if callback in self._progress_callbacks:
            self._progress_callbacks.remove(callback)
    
    def unregister_error_callback(self, callback: ErrorCallback) -> None:
        """
        Unregister an error callback.
        
        Args:
            callback: Error callback to unregister
        """
        if callback in self._error_callbacks:
            self._error_callbacks.remove(callback)
    
    def unregister_completion_callback(
        self,
        callback: CompletionCallback
    ) -> None:
        """
        Unregister a completion callback.
        
        Args:
            callback: Completion callback to unregister
        """
        if callback in self._completion_callbacks:
            self._completion_callbacks.remove(callback)
    
    def notify_progress(self, context: ProgressContext) -> None:
        """
        Notify all registered progress callbacks.
        
        Args:
            context: Progress context to send
        """
        for callback in self._progress_callbacks:
            try:
                callback.on_progress(context)
            except Exception as e:
                # Log but don't let callback errors break the operation
                logger.error(
                    "Error in progress callback %s: %s",
                    callback,
                    e,
                    exc_info=True
                )
    
    def notify_error(self, context: ErrorContext) -> None:
        """
        Notify all registered error callbacks.
        
        Args:
            context: Error context to send
        """
        for callback in self._error_callbacks:
            try:
                callback.on_error(context)
            except Exception as e:
                # Log but don't let callback errors break the operation
                logger.error(
                    "Error in error callback %s: %s",
                    callback,
                    e,
                    exc_info=True
                )
    
    def notify_completion(self, context: CompletionContext) -> None:
        """
        Notify all registered completion callbacks.
        
        Args:
            context: Completion context to send
        """
        for callback in self._completion_callbacks:
            try:
                callback.on_completion(context)
            except Exception as e:
                # Log but don't let callback errors break the operation
                logger.error(
                    "Error in completion callback %s: %s",
                    callback,
                    e,
                    exc_info=True
                )
    
    def clear_all_callbacks(self) -> None:
        """Clear all registered callbacks."""
        self._progress_callbacks.clear()
        self._error_callbacks.clear()
        self._completion_callbacks.clear()
