"""
StorageFactory: Factory pattern para crear origins de almacenamiento.

Proporciona métodos factory para crear instancias de los diferentes orígenes
de almacenamiento, con soporte para obtener credenciales de variables de
entorno o parámetros directos.
"""
from typing import Optional, Union

from tai_alphi import Alphi
from tai_storage.secrets import SecretManager
from tai_storage.implementations.local import LocalOrigin
from tai_storage.implementations.sharepoint import SharePointOrigin
from tai_storage.implementations.azure import AzureBlobOrigin
from tai_storage.cache import CachedOrigin
from tai_storage.base import Origin

logger = Alphi.get_logger_by_name('tai-storage')


class StorageFactory:
    """
    Factory para crear instancias de Origins de almacenamiento.
    
    Proporciona métodos estáticos para crear y configurar diferentes
    tipos de orígenes de almacenamiento (local, SharePoint, Azure, etc.)
    con soporte automático para obtener credenciales de variables de entorno.
    
    Soporta modo desarrollo (dev_mode) que automáticamente envuelve los
    origins con CachedOrigin para acelerar el desarrollo con caché local.
    
    Examples:
        >>> # Crear origen local
        >>> origin = StorageFactory.create_local(root_path='/data')
        
        >>> # Activar modo desarrollo globalmente
        >>> StorageFactory.set_dev_mode(True)
        >>> origin = StorageFactory.create_sharepoint()  # Automáticamente usa caché
        
        >>> # O activar dev_mode por origin específico
        >>> origin = StorageFactory.create_azure(
        ...     account_name='myaccount',
        ...     account_key='mykey',
        ...     dev_mode=True,
        ...     dev_cache_path='.cache'
        ... )
    """
    
    # Configuración global de modo desarrollo
    _dev_mode_enabled: bool = False
    _dev_cache_path: str = '.cached_storage'
    
    # Configuración global de modo desarrollo
    _dev_mode_enabled: bool = False
    _dev_cache_path: str = '.cached_storage'
    
    @classmethod
    def set_dev_mode(cls, enabled: bool = True, cache_path: str = '.cached_storage') -> None:
        """
        Activa o desactiva el modo desarrollo globalmente.
        
        Cuando está activo, todos los origins creados se envuelven
        automáticamente en CachedOrigin para usar caché local.
        
        Args:
            enabled: True para activar, False para desactivar
            cache_path: Ruta donde almacenar el caché (default: '.cached_storage')
            
        Examples:
            >>> StorageFactory.set_dev_mode(True)
            >>> origin = StorageFactory.create_azure(...)  # Usa caché automáticamente
            >>> StorageFactory.set_dev_mode(False)  # Desactivar
        """
        cls._dev_mode_enabled = enabled
        cls._dev_cache_path = cache_path
        
        if enabled:
            logger.info(f"Modo desarrollo activado. Caché en: {cache_path}")
        else:
            logger.info("Modo desarrollo desactivado")
    
    @classmethod
    def _wrap_with_cache_if_needed(
        cls,
        origin: Origin,
        dev_mode: Optional[bool] = None,
        dev_cache_path: Optional[str] = None
    ) -> Union[Origin, CachedOrigin]:
        """
        Envuelve el origin con CachedOrigin si el modo desarrollo está activo.
        
        Args:
            origin: Origin a envolver
            dev_mode: Si se especifica, sobrescribe la configuración global
            dev_cache_path: Ruta del caché (si no se especifica, usa la global)
            
        Returns:
            CachedOrigin si dev_mode está activo, Origin original si no
        """
        # Determinar si usar dev_mode
        use_dev_mode = dev_mode if dev_mode is not None else cls._dev_mode_enabled
        
        if not use_dev_mode:
            return origin
        
        # Determinar ruta de caché
        cache_path = dev_cache_path if dev_cache_path is not None else cls._dev_cache_path
        
        logger.info(f"Envolviendo origin con caché (dev_mode): {cache_path}")
        return CachedOrigin(origin, cache_path=cache_path)
    
    @classmethod
    def create_local(
        cls,
        root_path: Optional[str] = None,
        auto_connect: bool = True,
        dev_mode: Optional[bool] = None,
        dev_cache_path: Optional[str] = None
    ) -> Union[LocalOrigin, CachedOrigin]:
        """
        Crea un origen de almacenamiento local.
        
        Args:
            root_path: Ruta raíz del almacenamiento.
                Si no se proporciona, se obtiene de la variable de entorno
                STORAGE_LOCAL_ROOT_PATH
            auto_connect: Si True, conecta automáticamente el origen
            dev_mode: Si True, envuelve el origin con CachedOrigin.
                Si None, usa la configuración global de set_dev_mode()
            dev_cache_path: Ruta del caché (default: '.cached_storage')
            
        Returns:
            LocalOrigin configurado, o CachedOrigin si dev_mode está activo
            
        Raises:
            SecretNotFoundError: Si root_path no se proporciona ni está en env
            
        Examples:
            >>> origin = StorageFactory.create_local(root_path='/data/files')
            >>> folder = origin.get_folder('documents')
            
            >>> # Usando variable de entorno STORAGE_LOCAL_ROOT_PATH
            >>> origin = StorageFactory.create_local()
        """
        root_path = SecretManager.get_secret(
            value=root_path,
            env_var='STORAGE_LOCAL_ROOT_PATH',
            required=True
        )
        
        logger.info(f"Creando LocalOrigin con root: {root_path}")
        origin = LocalOrigin(root_path=root_path)
        
        if auto_connect:
            origin.connect()
        
        return cls._wrap_with_cache_if_needed(origin, dev_mode, dev_cache_path)
    
    @classmethod
    def create_sharepoint(
        cls,
        tenant_id: Optional[str] = None,
        client_id: Optional[str] = None,
        client_secret: Optional[str] = None,
        site_name: Optional[str] = None,
        hostname: Optional[str] = None,
        auto_connect: bool = True,
        dev_mode: Optional[bool] = None,
        dev_cache_path: Optional[str] = None
    ) -> Union[SharePointOrigin, CachedOrigin]:
        """
        Crea un origen de SharePoint Online usando Microsoft Graph API.
        
        Args:
            tenant_id: ID del tenant de Azure AD.
                Env var: SHAREPOINT_TENANT_ID
            client_id: Application (client) ID de Azure AD.
                Env var: SHAREPOINT_CLIENT_ID
            client_secret: Client secret de la aplicación.
                Env var: SHAREPOINT_CLIENT_SECRET
            site_name: Nombre del sitio de SharePoint.
                Env var: SHAREPOINT_SITE_NAME
            hostname: Hostname de SharePoint (ej: contoso.sharepoint.com).
                Env var: SHAREPOINT_HOSTNAME
            auto_connect: Si True, conecta automáticamente el origen
            dev_mode: Si True, envuelve el origin con CachedOrigin.
                Si None, usa la configuración global de set_dev_mode()
            dev_cache_path: Ruta del caché (default: '.cached_storage')
            
        Returns:
            SharePointOrigin configurado, o CachedOrigin si dev_mode está activo
            
        Raises:
            SecretNotFoundError: Si alguna credencial requerida no se encuentra
            
        Examples:
            >>> origin = StorageFactory.create_sharepoint(
            ...     tenant_id='your-tenant-id',
            ...     client_id='your-client-id',
            ...     client_secret='your-secret',
            ...     site_name='Documents',
            ...     hostname='contoso.sharepoint.com'
            ... )
            
            >>> # Usando variables de entorno
            >>> origin = StorageFactory.create_sharepoint()
        """
        tenant_id = SecretManager.get_secret(
            value=tenant_id,
            env_var='SHAREPOINT_TENANT_ID',
            required=True
        )
        
        client_id = SecretManager.get_secret(
            value=client_id,
            env_var='SHAREPOINT_CLIENT_ID',
            required=True
        )
        
        client_secret = SecretManager.get_secret(
            value=client_secret,
            env_var='SHAREPOINT_CLIENT_SECRET',
            required=True
        )
        
        site_name = SecretManager.get_secret(
            value=site_name,
            env_var='SHAREPOINT_SITE_NAME',
            required=True
        )
        
        hostname = SecretManager.get_secret(
            value=hostname,
            env_var='SHAREPOINT_HOSTNAME',
            required=True
        )
        
        logger.info(f"Creando SharePointOrigin para {hostname}/{site_name}")
        origin = SharePointOrigin(
            tenant_id=tenant_id,
            client_id=client_id,
            client_secret=client_secret,
            site_name=site_name,
            hostname=hostname
        )
        
        if auto_connect:
            origin.connect()
        
        return cls._wrap_with_cache_if_needed(origin, dev_mode, dev_cache_path)
    
    @classmethod
    def create_azure(
        cls,
        account_name: Optional[str] = None,
        account_key: Optional[str] = None,
        connection_string: Optional[str] = None,
        auto_connect: bool = True,
        dev_mode: Optional[bool] = None,
        dev_cache_path: Optional[str] = None
    ) -> Union[AzureBlobOrigin, CachedOrigin]:
        """
        Crea un origen de Azure Blob Storage.
        
        Puede usar connection_string directamente, o construirlo desde
        account_name + account_key.
        
        Args:
            account_name: Nombre de la cuenta de Azure Storage.
                Env var: AZURE_STORAGE_ACCOUNT_NAME
            account_key: Key de acceso de la cuenta.
                Env var: AZURE_STORAGE_ACCOUNT_KEY
            connection_string: String de conexión completo (alternativa).
                Env var: AZURE_STORAGE_CONNECTION_STRING
            auto_connect: Si True, conecta automáticamente el origen
            dev_mode: Si True, envuelve el origin con CachedOrigin.
                Si None, usa la configuración global de set_dev_mode()
            dev_cache_path: Ruta del caché (default: '.cached_storage')
            
        Returns:
            AzureBlobOrigin configurado, o CachedOrigin si dev_mode está activo
            
        Raises:
            SecretNotFoundError: Si no se proporciona connection_string
                ni (account_name + account_key)
            
        Examples:
            >>> # Usando connection string
            >>> origin = StorageFactory.create_azure(
            ...     connection_string='DefaultEndpointsProtocol=https;...'
            ... )
            
            >>> # Usando account name + key
            >>> origin = StorageFactory.create_azure(
            ...     account_name='mystorageaccount',
            ...     account_key='mykey123...'
            ... )
            
            >>> # Usando variables de entorno
            >>> origin = StorageFactory.create_azure()
        """
        # Opción 1: connection_string directo
        connection_string = SecretManager.get_secret(
            value=connection_string,
            env_var='AZURE_STORAGE_CONNECTION_STRING',
            required=False
        )
        
        if connection_string:
            logger.info("Creando AzureBlobOrigin con connection_string")
            origin = AzureBlobOrigin(connection_string=connection_string)
        else:
            # Opción 2: account_name + account_key
            account_name = SecretManager.get_secret(
                value=account_name,
                env_var='AZURE_STORAGE_ACCOUNT_NAME',
                required=True
            )
            
            account_key = SecretManager.get_secret(
                value=account_key,
                env_var='AZURE_STORAGE_ACCOUNT_KEY',
                required=True
            )
            
            logger.info(f"Creando AzureBlobOrigin para cuenta: {account_name}")
            origin = AzureBlobOrigin(
                account_name=account_name,
                account_key=account_key
            )
        
        if auto_connect:
            origin.connect()
        
        return cls._wrap_with_cache_if_needed(origin, dev_mode, dev_cache_path)
    
    # Alias para create_azure
    create_blobstorage = create_azure
