"""
Pipe abstraction for Fustor.

A Pipe represents the runtime binding between data sources/receivers
and data sinks/views. It manages the lifecycle of data transfer tasks.
"""
from abc import ABC, abstractmethod
from enum import IntFlag, auto
from typing import Optional, Dict, Any, TYPE_CHECKING
import logging

if TYPE_CHECKING:
    from .context import PipeContext
    from .handler import Handler

logger = logging.getLogger(__name__)


class PipeState(IntFlag):
    """
    Pipe state flags using bitmask for composite states.
    
    States can be combined, e.g., RUNNING | CONF_OUTDATED
    """
    STOPPED = 0
    INITIALIZING = auto()
    RUNNING = auto()
    PAUSED = auto()
    ERROR = auto()
    CONF_OUTDATED = auto()  # Configuration has changed, needs restart
    SNAPSHOT_SYNC = auto()  # Currently in snapshot sync phase
    MESSAGE_SYNC = auto()   # Currently in message/realtime phase
    AUDIT_PHASE = auto()     # Currently in audit phase
    RECONNECTING = auto()    # Currently attempting to reconnect
    DRAINING = auto()        # Draining queues before stopping
    STOPPING = auto()        # Gracefully stopping


class Pipe(ABC):
    """
    Abstract base class for all Pipes.
    
    A Pipe orchestrates:
    - Session lifecycle management
    - Handler invocation (Source/Sender or Receiver/View)
    - Heartbeat and timeout handling
    - Error recovery
    
    Agent Pipe: Source -> Sender
    Fusion Pipe: Receiver -> View(s)
    """
    
    def __init__(
        self,
        pipe_id: str,
        config: Dict[str, Any],
        context: Optional["PipeContext"] = None
    ):
        """
        Initialize the pipe.
        
        Args:
            pipe_id: Unique identifier for this pipe
            config: Pipe configuration dictionary
            context: Optional shared context for cross-pipe coordination
        """
        self.id = pipe_id
        self.config = config
        self.context = context
        self.state = PipeState.STOPPED
        self.info: Optional[str] = None  # Human-readable status info
        self.logger = logging.getLogger(f"{__name__}.{pipe_id}")
        
        # Session tracking
        self.session_id: Optional[str] = None
        self.session_timeout_seconds: int = config.get("session_timeout_seconds", 30)
        
    def __str__(self) -> str:
        return f"Pipe({self.id}, state={self.state.name})"
    
    def _set_state(self, new_state: PipeState, info: Optional[str] = None):
        """Update pipestate with optional info message."""
        old_state = self.state
        self.state = new_state
        if info:
            self.info = info
        if PipeState.ERROR in new_state:
            self.logger.warning(f"State changed: {old_state.name} -> {new_state.name}" + 
                            (f" ({info})" if info else ""))
        else:
            self.logger.info(f"State changed: {old_state.name} -> {new_state.name}" + 
                            (f" ({info})" if info else ""))
    
    @abstractmethod
    async def start(self) -> None:
        """
        Start the pipe.
        
        This should:
        1. Initialize handlers
        2. Establish session (if applicable)
        3. Begin data transfer loop
        """
        raise NotImplementedError
    
    @abstractmethod
    async def stop(self) -> None:
        """
        Stop the pipe gracefully.
        
        This should:
        1. Stop data transfer loop
        2. Close session (if applicable)
        3. Release resources
        """
        raise NotImplementedError
    
    async def restart(self) -> None:
        """Restart the pipe (stop then start)."""
        await self.stop()
        await self.start()
    
    @abstractmethod
    async def on_session_created(self, session_id: str, **kwargs) -> None:
        """
        Called when a session is successfully created.
        
        Args:
            session_id: The session identifier
            **kwargs: Additional session metadata (timeout, role, etc.)
        """
        raise NotImplementedError
    
    @abstractmethod
    async def on_session_closed(self, session_id: str) -> None:
        """
        Called when a session is closed (normally or due to timeout).
        
        Args:
            session_id: The session identifier
        """
        raise NotImplementedError
    
    def is_running(self) -> bool:
        """Check if pipe is in a running state."""
        return bool(self.state & (PipeState.RUNNING | 
                                  PipeState.PAUSED |
                                  PipeState.RECONNECTING |
                                  PipeState.SNAPSHOT_SYNC | 
                                  PipeState.MESSAGE_SYNC | 
                                  PipeState.AUDIT_PHASE))
    
    def has_active_session(self) -> bool:
        """Check if pipe has an active session with the remote peer."""
        return self.session_id is not None

    def is_outdated(self) -> bool:
        """Check if pipe configuration is outdated."""
        return bool(self.state & PipeState.CONF_OUTDATED)
    
    def get_dto(self) -> Dict[str, Any]:
        """Get a data transfer object representation of the pipe."""
        return {
            "id": self.id,
            "state": self.state.name,
            "info": self.info,
            "session_id": self.session_id,
            "session_timeout_seconds": self.session_timeout_seconds,
        }
