"""Wrapper de caché para File en modo desarrollo."""

from __future__ import annotations
from typing import TYPE_CHECKING, Dict, Any, BinaryIO
from datetime import datetime
import io

if TYPE_CHECKING:
    from .cached_folder import CachedFolder

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

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


class CachedFile(File):
    """
    Wrapper que agrega caché local a cualquier File.
    
    Hereda de File para mantener la interfaz consistente.
    Las operaciones de lectura primero buscan en caché local.
    Si no existe, descarga desde el origen remoto y lo cachea.
    Las operaciones de escritura actualizan tanto remoto como caché.
    """
    
    def __init__(self, remote_file: File, cache_file: File, folder: 'CachedFolder'):
        """
        Inicializa el archivo con caché.
        
        Args:
            remote_file: Archivo remoto original
            cache_file: Archivo local que actúa como caché
            folder: CachedFolder padre
        """
        super().__init__(folder, remote_file.filename, remote_file.full_path)
        self._remote = remote_file
        self._cache = cache_file
        
        logger.debug(f"CachedFile inicializado: {remote_file.filename}")
    
    def _ensure_cached(self) -> None:
        """
        Asegura que el archivo existe en caché y está actualizado.
        Compara la fecha de modificación del remoto con el caché.
        """
        # Verificar si el archivo existe en remoto
        if not self._remote.exists():
            # Si no existe en remoto pero sí en caché, invalidar caché
            if self._cache.exists():
                logger.warning(f"Archivo eliminado en remoto, invalidando caché: {self._remote.filename}")
                self._cache.delete()
            logger.debug(f"Archivo no existe en remoto: {self._remote.filename}")
            return
        
        # Si no existe en caché, descargar
        if not self._cache.exists():
            logger.info(f"Descargando a caché: {self._remote.filename}")
            data = self._remote.read()
            self._cache.write(data)
            logger.debug(f"Archivo cacheado: {self._cache.full_path} ({len(data)} bytes)")
            return
        
        # Ambos existen: comparar fechas de modificación
        try:
            remote_modified = self._remote.modified_time
            cache_modified = self._cache.modified_time
            
            if remote_modified and cache_modified:
                if remote_modified > cache_modified:
                    # Archivo remoto es más reciente, actualizar caché
                    logger.info(f"Archivo remoto más reciente, actualizando caché: {self._remote.filename}")
                    logger.debug(f"Remoto: {remote_modified}, Caché: {cache_modified}")
                    data = self._remote.read()
                    self._cache.write(data)
                    logger.debug(f"Caché actualizado ({len(data)} bytes)")
                else:
                    # Caché está actualizado
                    logger.debug(f"Usando caché actualizado: {self._remote.filename}")
            else:
                # No podemos comparar fechas, usar caché conservadoramente
                logger.debug(f"No se pueden comparar fechas, usando caché existente: {self._remote.filename}")
        except Exception as e:
            # Si hay error al comparar fechas, usar caché conservadoramente
            logger.debug(f"Error comparando fechas, usando caché: {e}")
    
    # ── Propiedades abstractas de File ──
    
    @property
    def name(self) -> str:
        """Nombre del archivo sin extensión."""
        return self._remote.name
    
    @property
    def extension(self) -> str:
        """Extensión del archivo."""
        return self._remote.extension
    
    @property
    def size(self) -> int:
        """Tamaño del archivo en bytes."""
        return self._remote.size
    
    @property
    def created_time(self) -> datetime:
        """Fecha de creación del archivo."""
        return self._remote.created_time
    
    @property
    def modified_time(self) -> datetime:
        """Fecha de última modificación del archivo."""
        return self._remote.modified_time
    
    # ── Métodos abstractos de File ──
    
    def read(self) -> bytes:
        """
        Lee el contenido del archivo.
        Primero busca en caché, si no existe lo descarga.
        
        Returns:
            Contenido del archivo en bytes
        """
        self._ensure_cached()
        
        if self._cache.exists():
            logger.debug(f"Leyendo desde caché: {self._remote.filename}")
            return self._cache.read()
        else:
            logger.error(f"Archivo no encontrado: {self._remote.filename}")
            raise FileNotFoundError(f"El archivo no existe: {self._remote.filename}")
    
    def read_text(self, encoding: str = 'utf-8') -> str:
        """
        Lee el contenido del archivo como texto.
        
        Args:
            encoding: Codificación del archivo (default: utf-8)
            
        Returns:
            Contenido del archivo como string
        """
        self._ensure_cached()
        
        if self._cache.exists():
            logger.debug(f"Leyendo texto desde caché: {self._remote.filename}")
            return self._cache.read_text(encoding=encoding)
        else:
            raise FileNotFoundError(f"El archivo no existe: {self._remote.filename}")
    
    def read_stream(self) -> BinaryIO:
        """
        Obtiene un stream de lectura del archivo.
        Usa el caché si está disponible.
        
        Returns:
            Stream binario de lectura (BytesIO)
        """
        content = self.read()
        return io.BytesIO(content)
    
    def write(self, data: bytes, overwrite: bool = True) -> None:
        """
        Escribe datos en el archivo.
        Actualiza tanto el archivo remoto como el caché.
        
        Args:
            data: Datos a escribir
            overwrite: Si True, sobrescribe; si False, lanza error si existe
        """
        logger.info(f"Escribiendo archivo (remoto + caché): {self._remote.filename}")
        
        # Escribir en remoto
        self._remote.write(data, overwrite=overwrite)
        
        # Actualizar caché (siempre sobrescribir el caché local)
        self._cache.write(data, overwrite=True)
        
        logger.debug(f"Archivo actualizado: {len(data)} bytes")
    
    def write_text(self, text: str, encoding: str = 'utf-8', overwrite: bool = True) -> None:
        """
        Escribe texto en el archivo.
        
        Args:
            text: Texto a escribir
            encoding: Codificación del archivo (default: utf-8)
            overwrite: Si True, sobrescribe; si False, lanza error si existe
        """
        logger.info(f"Escribiendo texto (remoto + caché): {self._remote.filename}")
        
        # Escribir en remoto
        self._remote.write_text(text, encoding=encoding, overwrite=overwrite)
        
        # Actualizar caché (siempre sobrescribir el caché local)
        self._cache.write_text(text, encoding=encoding, overwrite=True)
        
        logger.debug(f"Texto actualizado: {len(text)} caracteres")
    
    def delete(self) -> None:
        """
        Elimina el archivo tanto del origen remoto como del caché.
        """
        logger.warning(f"Eliminando archivo (remoto + caché): {self._remote.filename}")
        
        # Eliminar del remoto
        if self._remote.exists():
            self._remote.delete()
        
        # Eliminar del caché
        if self._cache.exists():
            self._cache.delete()
        
        logger.info(f"Archivo eliminado: {self._remote.filename}")
    
    def exists(self) -> bool:
        """
        Verifica si el archivo existe en el origen remoto.
        
        Returns:
            True si existe, False si no
        """
        return self._remote.exists()
    
    def copy_to(self, destination: File, overwrite: bool = False) -> None:
        """
        Copia el archivo a otro destino.
        Usa el caché si está disponible para evitar descarga.
        
        Args:
            destination: Archivo destino
            overwrite: Si True, sobrescribe si existe
        """
        logger.info(f"Copiando archivo: {self._remote.filename} -> {destination.filename}")
        
        # Si hay un CachedFile como destino, acceder al remoto
        if isinstance(destination, CachedFile):
            dest_remote = destination._remote
        else:
            dest_remote = destination
        
        # Copiar desde remoto a remoto
        self._remote.copy_to(dest_remote, overwrite=overwrite)
        
        # Actualizar caché del destino si es CachedFile
        if isinstance(destination, CachedFile):
            if self._cache.exists():
                data = self._cache.read()
                destination._cache.write(data)
                logger.debug(f"Caché del destino actualizado")
    
    def move_to(self, destination: File, overwrite: bool = False) -> None:
        """
        Mueve el archivo a otro destino.
        
        Args:
            destination: Archivo destino
            overwrite: Si True, sobrescribe si existe
        """
        logger.info(f"Moviendo archivo: {self._remote.filename} -> {destination.filename}")
        
        # Si hay un CachedFile como destino, acceder al remoto
        if isinstance(destination, CachedFile):
            dest_remote = destination._remote
        else:
            dest_remote = destination
        
        # Mover en remoto
        self._remote.move_to(dest_remote, overwrite=overwrite)
        
        # Eliminar del caché origen
        if self._cache.exists():
            self._cache.delete()
        
        # Actualizar caché del destino si es CachedFile
        if isinstance(destination, CachedFile) and dest_remote.exists():
            data = dest_remote.read()
            destination._cache.write(data)
    
    def get_metadata(self) -> Dict[str, Any]:
        """
        Obtiene todos los metadatos del archivo (delegando al remoto).
        
        Returns:
            Diccionario con metadatos
        """
        return self._remote.get_metadata()
    
    def __repr__(self) -> str:
        """Representación del archivo."""
        return f"CachedFile({self._remote.filename})"
