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

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

from tai_alphi import Alphi
from ...base.file import File
from ...exceptions import FileNotFoundError, WriteError

if TYPE_CHECKING:
    from .folder import AzureBlobFolder

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


class AzureBlobFile(File):
    """
    Representa un blob en Azure Blob Storage.
    
    Args:
        folder: AzureBlobFolder que contiene el blob
        filename: Nombre relativo del archivo
        blob_name: Nombre completo del blob en el container
    """
    
    def __init__(self, folder: AzureBlobFolder, filename: str, blob_name: str):
        super().__init__(folder, filename, blob_name)
        self.blob_name = blob_name
        self._blob_client: Optional[BlobClient] = None
        self._properties: Optional[Dict[str, Any]] = None
    
    def _get_blob_client(self) -> BlobClient:
        """Obtiene el BlobClient para este blob."""
        if not self._blob_client:
            container_client = self.folder._get_container_client()
            self._blob_client = container_client.get_blob_client(self.blob_name)
        return self._blob_client
    
    def exists(self) -> bool:
        """Verifica si el blob existe."""
        try:
            blob_client = self._get_blob_client()
            return blob_client.exists()
        except Exception:
            return False
    
    def _get_properties(self, force_refresh: bool = False) -> Dict[str, Any]:
        """Obtiene las propiedades del blob."""
        if self._properties and not force_refresh:
            return self._properties
        
        try:
            blob_client = self._get_blob_client()
            props = blob_client.get_blob_properties()
            
            # Convertir a dict para facilitar acceso
            self._properties = {
                'name': props.name,
                'size': props.size,
                'content_type': props.content_settings.content_type if props.content_settings else None,
                'last_modified': props.last_modified,
                'creation_time': props.creation_time,
                'metadata': props.metadata or {},
                'etag': props.etag,
                'lease': props.lease if hasattr(props, 'lease') else None,
            }
            
            return self._properties
            
        except ResourceNotFoundError:
            raise FileNotFoundError(f"El blob '{self.filename}' no existe")
        except Exception as e:
            raise FileNotFoundError(f"Error obteniendo propiedades: {e}")
    
    def read(self, mode: str = 'rb') -> bytes | str:
        """
        Lee el contenido del blob.
        
        Args:
            mode: 'rb' para binario, 'r' para texto
            
        Returns:
            Contenido del blob (bytes o str según mode)
        """
        try:
            blob_client = self._get_blob_client()
            download_stream = blob_client.download_blob()
            content = download_stream.readall()
            
            if mode == 'r':
                return content.decode('utf-8')
            
            return content
            
        except ResourceNotFoundError:
            raise FileNotFoundError(f"El blob '{self.filename}' no existe")
        except Exception as e:
            raise FileNotFoundError(f"Error leyendo blob: {e}")
    
    def write(self, content: bytes | str, mode: str = 'wb') -> None:
        """
        Escribe contenido en el blob.
        
        Args:
            content: Contenido a escribir (bytes o str)
            mode: 'wb' para binario, 'w' para texto
        """
        # Convertir a bytes si es necesario
        if isinstance(content, str):
            data = content.encode('utf-8')
        else:
            data = content
        
        try:
            blob_client = self._get_blob_client()
            blob_client.upload_blob(data, overwrite=True)
            
            # Invalidar caché de propiedades
            self._properties = None
            
            logger.info(f"✓ Blob escrito: {self.filename}")
            
        except Exception as e:
            raise WriteError(f"Error escribiendo blob: {e}")
    
    def delete(self) -> None:
        """Elimina el blob de Azure Blob Storage."""
        try:
            blob_client = self._get_blob_client()
            blob_client.delete_blob()
            logger.info(f"✓ Blob eliminado: {self.filename}")
            
        except ResourceNotFoundError:
            raise FileNotFoundError(f"El blob '{self.filename}' no existe")
        except Exception as e:
            raise WriteError(f"Error eliminando blob: {e}")
    
    def copy_to(self, destination: File, overwrite: bool = False) -> None:
        """
        Copia el blob a otro destino.
        
        Args:
            destination: Archivo destino (puede ser AzureBlobFile u otro)
            overwrite: Si True, sobrescribe si existe
        """
        # Si el destino es también AzureBlobFile, usar copy nativa de Azure
        from .file import AzureBlobFile
        
        if isinstance(destination, AzureBlobFile):
            self._copy_native(destination, overwrite)
        else:
            # Fallback: leer y escribir
            content = self.read(mode='rb')
            
            if destination.exists() and not overwrite:
                raise WriteError(f"El archivo destino '{destination.filename}' ya existe")
            
            destination.write(content, mode='wb')
        
        logger.info(f"✓ Blob copiado: {self.filename} → {destination.filename}")
    
    def _copy_native(self, destination: AzureBlobFile, overwrite: bool) -> None:
        """Copia el blob usando la API nativa de Azure."""
        if destination.exists() and not overwrite:
            raise WriteError(f"El blob destino '{destination.filename}' ya existe")
        
        try:
            # Obtener URL del blob fuente
            source_blob_client = self._get_blob_client()
            source_url = source_blob_client.url
            
            # Copiar al destino
            dest_blob_client = destination._get_blob_client()
            dest_blob_client.start_copy_from_url(source_url)
            
            logger.info(f"✓ Blob copiado (nativo): {self.filename} → {destination.filename}")
            
        except Exception as e:
            raise WriteError(f"Error copiando blob: {e}")
    
    def move_to(self, destination: File, overwrite: bool = False) -> None:
        """
        Mueve el blob a otro destino.
        
        Args:
            destination: Archivo destino
            overwrite: Si True, sobrescribe si existe
        """
        # Copiar y luego eliminar
        self.copy_to(destination, overwrite)
        self.delete()
        logger.info(f"✓ Blob movido: {self.filename} → {destination.filename}")
    
    @property
    def name(self) -> str:
        """Nombre del blob sin extensión."""
        return Path(self.filename).stem
    
    @property
    def extension(self) -> str:
        """Extensión del blob (incluye el punto)."""
        return Path(self.filename).suffix
    
    @property
    def size(self) -> int:
        """Tamaño del blob en bytes."""
        props = self._get_properties()
        return props.get('size', 0)
    
    @property
    def created_time(self) -> datetime:
        """Fecha de creación del blob."""
        props = self._get_properties()
        return props.get('creation_time', datetime.now())
    
    @property
    def modified_time(self) -> datetime:
        """Fecha de última modificación del blob."""
        props = self._get_properties()
        return props.get('last_modified', datetime.now())
    
    @property
    def metadata(self) -> Dict[str, Any]:
        """
        Metadatos completos del blob.
        
        Returns:
            Dict con todos los metadatos disponibles
        """
        return self._get_properties()
    
    def set_metadata(self, metadata: Dict[str, str]) -> None:
        """
        Establece metadatos personalizados en el blob.
        
        Args:
            metadata: Dict con pares clave-valor (ambos deben ser strings)
        """
        try:
            blob_client = self._get_blob_client()
            blob_client.set_blob_metadata(metadata)
            
            # Invalidar caché
            self._properties = None
            
            logger.info(f"✓ Metadata actualizado en: {self.filename}")
            
        except Exception as e:
            raise WriteError(f"Error estableciendo metadata: {e}")
    
    def get_metadata(self, key: Optional[str] = None) -> Any:
        """
        Obtiene metadatos del blob.
        
        Args:
            key: Clave específica (opcional). Si None, devuelve todos.
            
        Returns:
            Valor de la clave o dict completo de metadata
        """
        props = self._get_properties()
        metadata = props.get('metadata', {})
        
        if key:
            return metadata.get(key)
        return metadata
