"""
Implementación de Folder para Azure Blob Storage.
"""
from __future__ import annotations
from typing import List, Optional, Dict, Any, TYPE_CHECKING
from datetime import datetime
from pathlib import Path
import io

from azure.storage.blob import ContainerClient
from azure.core.exceptions import ResourceNotFoundError

from tai_alphi import Alphi
from ...base.folder import Folder
from ...exceptions import FileNotFoundError, FolderNotFoundError, FolderAlreadyExistsError, UploadError

if TYPE_CHECKING:
    from .origin import AzureBlobOrigin
    from .file import AzureBlobFile

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


class AzureBlobFolder(Folder):
    """
    Representa un container o carpeta virtual en Azure Blob Storage.
    
    En Azure Blob Storage:
    - El primer nivel es un container real
    - Los niveles internos son prefijos virtuales (no existen físicamente)
    
    Args:
        origin: AzureBlobOrigin asociado
        path: "container" o "container/virtual/path"
    """
    
    def __init__(self, origin: AzureBlobOrigin, path: str):
        super().__init__(origin, path)
        
        # Separar container y prefix
        parts = path.split('/', 1)
        self.container_name = parts[0]
        self.prefix = parts[1] + '/' if len(parts) > 1 else ''
        
        self._container_client: Optional[ContainerClient] = None
    
    def _get_container_client(self) -> ContainerClient:
        """Obtiene el ContainerClient para este folder."""
        if not self._container_client:
            self._container_client = self.origin.client.get_container_client(
                self.container_name
            )
        return self._container_client
    
    def exists(self) -> bool:
        """
        Verifica si el container existe.
        
        Nota: Los prefijos virtuales no se verifican porque no existen físicamente.
        """
        try:
            container_client = self._get_container_client()
            return container_client.exists()
        except Exception:
            return False
    
    def list_files(
        self,
        pattern: Optional[str] = None,
        recursive: bool = False,
        modified_after: Optional[datetime] = None,
        modified_before: Optional[datetime] = None,
        created_after: Optional[datetime] = None,
        created_before: Optional[datetime] = None
    ) -> List['AzureBlobFile']:
        """
        Lista blobs como objetos AzureBlobFile con filtrado opcional.
        
        Args:
            pattern: Patrón regex para filtrar nombres
            recursive: Si True, busca recursivamente en subprefijos
            modified_after: Solo blobs modificados después de esta fecha
            modified_before: Solo blobs modificados antes de esta fecha
            created_after: Solo blobs creados después de esta fecha
            created_before: Solo blobs creados antes de esta fecha
            
        Returns:
            Lista de objetos AzureBlobFile que cumplen los criterios
        """
        import re
        from .file import AzureBlobFile
        
        container_client = self._get_container_client()
        files = []
        
        # Configurar delimiter para controlar recursión
        delimiter = None if recursive else '/'
        
        # Listar blobs con propiedades (necesarias para filtros de fecha)
        blob_iter = container_client.list_blobs(
            name_starts_with=self.prefix,
            include=['metadata']
        )
        
        for blob in blob_iter:
            blob_name = blob.name
            
            # Remover el prefix para obtener nombre relativo
            if self.prefix and blob_name.startswith(self.prefix):
                relative_name = blob_name[len(self.prefix):]
            else:
                relative_name = blob_name
            
            # En modo no recursivo, ignorar archivos en subdirectorios
            if not recursive and '/' in relative_name:
                continue
            
            # Filtrar por patrón
            if pattern and not re.search(pattern, relative_name, re.IGNORECASE):
                continue
            
            # Filtros de fecha
            modified_time = blob.last_modified
            created_time = blob.creation_time if hasattr(blob, 'creation_time') else modified_time
            
            # Hacer fechas timezone-aware si es necesario
            if modified_time and modified_time.tzinfo is None:
                from datetime import timezone
                modified_time = modified_time.replace(tzinfo=timezone.utc)
            if created_time and created_time.tzinfo is None:
                from datetime import timezone
                created_time = created_time.replace(tzinfo=timezone.utc)
            
            # Aplicar filtros de fecha
            if modified_after and modified_time <= modified_after:
                continue
            if modified_before and modified_time >= modified_before:
                continue
            if created_after and created_time <= created_after:
                continue
            if created_before and created_time >= created_before:
                continue
            
            # Crear objeto File
            file = AzureBlobFile(self, relative_name, blob_name)
            files.append(file)
        
        logger.debug(f"Encontrados {len(files)} archivos en {self.path}")
        return files
    
    def list_folders(self, pattern: Optional[str] = None) -> List['AzureBlobFolder']:
        """
        Lista las subcarpetas virtuales como objetos AzureBlobFolder.
        
        En Azure Blob Storage, las carpetas son virtuales (prefijos).
        Este método lista los prefijos únicos al siguiente nivel.
        
        Args:
            pattern: Patrón regex opcional para filtrar nombres
            
        Returns:
            Lista de objetos AzureBlobFolder
        """
        import re
        
        container_client = self._get_container_client()
        folders = []
        seen_names = set()
        
        # Listar con delimiter para obtener prefijos
        blob_iter = container_client.walk_blobs(
            name_starts_with=self.prefix,
            delimiter='/'
        )
        
        for item in blob_iter:
            # Los items sin atributo 'name' son prefijos (folders virtuales)
            if not hasattr(item, 'name') and hasattr(item, 'prefix'):
                prefix = item.prefix
                
                # Remover el prefix base
                if self.prefix and prefix.startswith(self.prefix):
                    relative_prefix = prefix[len(self.prefix):]
                else:
                    relative_prefix = prefix
                
                # Remover trailing slash y obtener solo el nombre de la carpeta
                folder_name = relative_prefix.rstrip('/').split('/')[-1]
                
                if folder_name and folder_name not in seen_names:
                    # Aplicar filtro de patrón si existe
                    if pattern and not re.search(pattern, folder_name, re.IGNORECASE):
                        continue
                    
                    seen_names.add(folder_name)
                    
                    # Crear objeto Folder
                    if self.prefix:
                        subfolder_path = f"{self.container_name}/{self.prefix}{folder_name}"
                    else:
                        subfolder_path = f"{self.container_name}/{folder_name}"
                    
                    folder = AzureBlobFolder(self.origin, subfolder_path)
                    folders.append(folder)
        
        logger.debug(f"Encontradas {len(folders)} subcarpetas en {self.path}")
        return folders
    
    def get_file(self, filename: str) -> AzureBlobFile:
        """
        Obtiene una referencia a un blob (archivo).
        
        Args:
            filename: Nombre del blob (puede incluir subpaths)
            
        Returns:
            AzureBlobFile
        """
        from .file import AzureBlobFile
        
        # Construir blob name completo
        blob_name = f"{self.prefix}{filename}"
        
        file = AzureBlobFile(self, filename, blob_name)
        
        if not file.exists():
            raise FileNotFoundError(f"El blob '{filename}' no existe en '{self.path}'")
        
        return file
    
    def upload_file(
        self,
        source: Any,
        destination: str,
        overwrite: bool = False
    ) -> AzureBlobFile:
        """
        Sube un blob al container.
        
        Args:
            source: Puede ser Path, str (ruta), bytes, o file-like object
            destination: Nombre del blob destino (puede incluir subpaths)
            overwrite: Si True, sobrescribe si existe
            
        Returns:
            AzureBlobFile subido
        """
        from .file import AzureBlobFile
        
        # Convertir source a bytes o stream
        if isinstance(source, (str, Path)):
            with open(source, 'rb') as f:
                data = f.read()
        elif isinstance(source, bytes):
            data = source
        elif hasattr(source, 'read'):
            data = source
        else:
            raise UploadError(f"Tipo de source no soportado: {type(source)}")
        
        # Construir blob name completo
        blob_name = f"{self.prefix}{destination}"
        
        # Verificar si existe
        container_client = self._get_container_client()
        blob_client = container_client.get_blob_client(blob_name)
        
        if blob_client.exists() and not overwrite:
            raise UploadError(
                f"El blob '{destination}' ya existe. Usa overwrite=True para reemplazar."
            )
        
        # Subir blob
        try:
            blob_client.upload_blob(data, overwrite=overwrite)
            logger.info(f"✓ Blob subido: {destination} en {self.path}")
        except Exception as e:
            raise UploadError(f"Error subiendo blob: {e}")
        
        return AzureBlobFile(self, destination, blob_name)
    
    def delete_file(self, filename: str) -> None:
        """
        Elimina un blob del container.
        
        Args:
            filename: Nombre del blob a eliminar
        """
        file = self.get_file(filename)
        file.delete()
    
    def get_metadata(self) -> Dict[str, Any]:
        """
        Obtiene metadatos del container/folder.
        
        Returns:
            Diccionario con metadatos del container
        """
        container_client = self._get_container_client()
        
        # Contar blobs con el prefix
        blob_count = 0
        for _ in container_client.list_blobs(name_starts_with=self.prefix):
            blob_count += 1
        
        metadata = {
            'path': self.path,
            'container_name': self.container_name,
            'prefix': self.prefix,
            'blob_count': blob_count
        }
        
        # Si es el container raíz, obtener propiedades
        if not self.prefix:
            try:
                props = container_client.get_container_properties()
                metadata['created_time'] = props.last_modified
                metadata['metadata'] = props.metadata
            except Exception as e:
                logger.warning(f"No se pudieron obtener propiedades del container: {e}")
        
        return metadata
    
    def create(self, parents: bool = True) -> None:
        """
        Crea el container.
        
        Nota: En Azure Blob Storage, solo los containers se crean físicamente.
        Los prefijos virtuales (subcarpetas) se crean automáticamente al subir blobs.
        
        Args:
            parents: No aplicable en Azure Blob (los prefijos se crean automáticamente)
            
        Raises:
            FolderAlreadyExistsError: Si el container ya existe
        """
        from ...exceptions import FolderAlreadyExistsError
        
        container_client = self._get_container_client()
        
        if container_client.exists():
            raise FolderAlreadyExistsError(f"El container '{self.container_name}' ya existe")
        
        # Crear el container
        container_client.create_container()
        logger.info(f"✓ Container creado: {self.container_name}")
        
        # Los prefijos virtuales no necesitan ser creados
        if self.prefix:
            logger.debug(f"El prefix '{self.prefix}' se creará automáticamente al subir blobs")
    
    def delete(self, recursive: bool = False) -> None:
        """
        Elimina el container o prefijo virtual.
        
        Nota: Si es un container raíz, se elimina el container completo con todo su contenido.
        Si es un prefijo virtual, se eliminan todos los blobs con ese prefijo.
        
        Args:
            recursive: Si True, elimina contenido recursivamente (siempre True para containers)
            
        Raises:
            FolderNotFoundError: Si el container no existe
        """
        container_client = self._get_container_client()
        
        if not container_client.exists():
            raise FolderNotFoundError(f"El container '{self.container_name}' no existe")
        
        if not self.prefix:
            # Es el container raíz, eliminarlo completamente
            container_client.delete_container()
            logger.info(f"✓ Container eliminado: {self.container_name}")
        else:
            # Es un prefijo virtual, eliminar todos los blobs con ese prefijo
            if not recursive:
                # Verificar que no hay blobs
                blobs = list(container_client.list_blobs(name_starts_with=self.prefix))
                if blobs:
                    raise FolderNotFoundError(
                        f"El prefijo '{self.prefix}' contiene {len(blobs)} blobs. "
                        f"Usa recursive=True para eliminarlos."
                    )
            else:
                # Eliminar todos los blobs con el prefijo
                blob_count = 0
                for blob in container_client.list_blobs(name_starts_with=self.prefix):
                    container_client.delete_blob(blob.name)
                    blob_count += 1
                
                logger.info(f"✓ Eliminados {blob_count} blobs con prefijo: {self.prefix}")
