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

from typing import TYPE_CHECKING, Optional
from pathlib import Path
from datetime import datetime

if TYPE_CHECKING:
    from ..base.file import File
    from .cached_folder import CachedFolder

from tai_alphi import Alphi
from ..data import FileDataReader  # Para get_data()

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


class CachedFile:
    """
    Wrapper que agrega caché local a cualquier File.
    
    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
        """
        self._remote = remote_file
        self._cache = cache_file
        self._folder = folder
        
        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}")
            pass
    
    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:
            # Si no está en caché ni en remoto, lanzar excepción
            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 write(self, data: bytes) -> None:
        """
        Escribe datos en el archivo.
        Actualiza tanto el archivo remoto como el caché.
        
        Args:
            data: Datos a escribir
        """
        logger.info(f"Escribiendo archivo (remoto + caché): {self._remote.filename}")
        
        # Escribir en remoto
        self._remote.write(data)
        
        # Actualizar caché
        self._cache.write(data)
        
        logger.debug(f"Archivo actualizado: {len(data)} bytes")
    
    def write_text(self, text: str, encoding: str = 'utf-8') -> None:
        """
        Escribe texto en el archivo.
        
        Args:
            text: Texto a escribir
            encoding: Codificación del archivo (default: utf-8)
        """
        logger.info(f"Escribiendo texto (remoto + caché): {self._remote.filename}")
        
        # Escribir en remoto
        self._remote.write_text(text, encoding=encoding)
        
        # Actualizar caché
        self._cache.write_text(text, encoding=encoding)
        
        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') -> None:
        """
        Copia el archivo a otro destino.
        Usa el caché si está disponible para evitar descarga.
        
        Args:
            destination: Archivo destino
        """
        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 (más eficiente en algunos casos)
        self._remote.copy_to(dest_remote)
        
        # Actualizar caché del destino si es CachedFile
        if isinstance(destination, CachedFile):
            if self._cache.exists():
                # Si tenemos caché local, copiarlo al caché del destino
                data = self._cache.read()
                destination._cache.write(data)
                logger.debug(f"Caché del destino actualizado")
    
    def move_to(self, destination: 'File') -> None:
        """
        Mueve el archivo a otro destino.
        
        Args:
            destination: Archivo destino
        """
        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)
        
        # 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)
    
    @property
    def filename(self) -> str:
        """Nombre del archivo."""
        return self._remote.filename
    
    @property
    def path(self) -> str:
        """Ruta completa del archivo."""
        return self._remote.full_path
    
    @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) -> Optional[datetime]:
        """Fecha de creación del archivo."""
        return self._remote.created_time
    
    @property
    def modified_time(self) -> Optional[datetime]:
        """Fecha de última modificación del archivo."""
        return self._remote.modified_time
    
    @property
    def metadata(self) -> dict:
        """Metadatos adicionales del archivo."""
        return self._remote.metadata
    
    def get_data(self) -> FileDataReader:
        """
        Obtiene un reader de datos para convertir el archivo a DataFrame.
        
        Usa el caché local cuando está disponible para lectura eficiente.
        
        Returns:
            FileDataReader: Objeto con métodos para convertir a DataFrame
            
        Raises:
            ImportError: Si el módulo 'data' no está instalado
            
        Examples:
            >>> # Auto-detectar formato
            >>> file = folder.get_file('datos.csv')
            >>> df = file.get_data().to_dataframe()
            
            >>> # CSV con opciones (lee desde caché si está disponible)
            >>> df = file.get_data().to_csv(sep=';', encoding='latin1')
            
            >>> # Excel con hoja específica
            >>> df = file.get_data().to_excel(sheet_name='Ventas')
        """
        logger.debug(f"Creando FileDataReader para archivo cacheado: {self.filename}")
        return FileDataReader(self)
    
    def __repr__(self) -> str:
        """Representación del archivo."""
        return f"CachedFile({self._remote.filename})"
