"""
DSS Context Module
==================

Singleton context manager for the DSS Plugin.
Handles configuration loading, mode detection, and strategy instantiation.
"""

import asyncio
import logging
from typing import Optional, Dict, Any

from .config import DSSConfig, DSSMode

# Logger setup
logger = logging.getLogger(__name__)

# Protocol/Type placeholder for Strategies (to be replaced by base class in next steps)
Strategy = Any


class DSSContext:
    """
    Singleton context manager for the DSS Plugin.

    Handles configuration loading, mode detection (Local/Remote),
    and strategy instantiation.
    """
    _instance: Optional['DSSContext'] = None
    _lock: asyncio.Lock = asyncio.Lock()

    def __init__(self) -> None:
        """
        Private initializer. Use get_instance() instead.
        """
        if DSSContext._instance is not None:
            raise RuntimeError("DSSContext is a singleton. Use get_instance() to access it.")

        self.config: Optional[DSSConfig] = None
        self.active_mode: DSSMode = DSSMode.REMOTE  # Default safe fallback
        self._capabilities: Dict[str, bool] = {}
        self._strategy_cache: Dict[str, Strategy] = {}
        self.session_id: Optional[str] = None

    @classmethod
    async def get_instance(cls) -> 'DSSContext':
        """
        Async factory method to get the singleton instance.
        Ensures config is loaded and mode is detected before returning.
        """
        if not cls._instance:
            async with cls._lock:
                # Double-check locking pattern
                if not cls._instance:
                    instance = cls()
                    await instance._initialize()
                    cls._instance = instance

        return cls._instance

    @classmethod
    def reset(cls) -> None:
        """
        Resets the singleton instance. Useful for testing.
        """
        cls._instance = None

    async def _initialize(self) -> None:
        """
        Internal initialization logic:
        1. Load Config
        2. Detect Mode
        3. Cache Capabilities
        """
        try:
            # 1. Load Configuration
            self.config = DSSConfig.load()
            self.session_id = self.config.session_id

            # 2. Detect Mode (Async check)
            self.active_mode = await self.config.get_active_mode()

            logger.info(f"DSSContext initialized. Mode: {self.active_mode.value}, Session: {self.session_id}")

            # 3. Cache Capabilities
            self._cache_capabilities()

        except Exception as e:
            logger.error(f"Failed to initialize DSSContext: {e}")
            # Fallback to defaults if initialization fails
            self.active_mode = DSSMode.REMOTE
            self._capabilities = {"limited": True}

    def _cache_capabilities(self) -> None:
        """
        Determines what the plugin can do based on the active mode.
        """
        # Base capabilities
        caps = {
            "can_read_files": False,
            "can_execute_browser": False,
            "can_screenshot": False,
            "can_connect_remote": True
        }

        if self.active_mode == DSSMode.LOCAL:
            # Local mode allows direct filesystem access and local browser control
            caps["can_read_files"] = True
            caps["can_execute_browser"] = True
            caps["can_screenshot"] = True
        elif self.active_mode == DSSMode.REMOTE:
            # Remote mode relies on API capabilities
            # Depending on remote configuration, these might differ
            caps["can_execute_browser"] = False  # Typically restricted in pure remote unless via API
            caps["can_read_files"] = False       # Security restriction

        self._capabilities = caps

    def get_capability(self, key: str) -> bool:
        """Check if a specific capability is active."""
        return self._capabilities.get(key, False)

    def get_api_url(self) -> str:
        """Get the correct API URL for the current mode."""
        if self.config is None:
            return "https://dss.overbits.luz.uy"  # Default fallback
        return self.config.get_api_url(self.active_mode)

    def get_strategy(self, strategy_type: str) -> Any:
        """
        Factory method to retrieve operational strategies.

        Args:
            strategy_type: One of 'browser', 'filesystem', 'screenshot'

        Returns:
            An instance of the requested strategy.
        """
        # Return cached strategy if available
        if strategy_type in self._strategy_cache:
            return self._strategy_cache[strategy_type]

        strategy_instance = None

        # NOTE: Strategy classes will be implemented in the next step.
        # We use local imports here to avoid circular dependency issues
        # if strategies define their own types using DSSContext.

        try:
            if strategy_type == "browser":
                # Will be implemented in Phase 2 & 3
                if self.active_mode == DSSMode.LOCAL:
                    from ..strategies.local.browser import LocalBrowserStrategy
                    strategy_instance = LocalBrowserStrategy(self)
                else:
                    from ..strategies.remote.browser import RemoteBrowserStrategy
                    strategy_instance = RemoteBrowserStrategy(self)

            elif strategy_type == "filesystem":
                # Will be implemented in Phase 2
                if self.active_mode == DSSMode.LOCAL:
                    from ..strategies.local.filesystem import LocalFilesystemStrategy
                    strategy_instance = LocalFilesystemStrategy(self)
                else:
                    from ..strategies.remote.filesystem import RemoteFilesystemStrategy
                    strategy_instance = RemoteFilesystemStrategy(self)

            elif strategy_type == "screenshot":
                # Screenshot is part of browser strategy
                return self.get_strategy("browser")

            else:
                raise ValueError(f"Unknown strategy type: {strategy_type}")

        except ImportError as e:
            logger.error(f"Failed to import strategy {strategy_type}: {e}")
            raise NotImplementedError(f"Strategy {strategy_type} not yet implemented") from e

        # Cache and return
        self._strategy_cache[strategy_type] = strategy_instance
        return strategy_instance
