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

from __future__ import annotations
from typing import TYPE_CHECKING, List, Optional, Union, Dict, Any
from pathlib import Path
from datetime import datetime
import os

if TYPE_CHECKING:
    from .cached_origin import CachedOrigin

from .cached_file import CachedFile
from ..base.folder import Folder
from tai_alphi import Alphi

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


class CachedFolder(Folder):
    """
    Wrapper que agrega caché local a cualquier Folder.
    
    Hereda de Folder para mantener la interfaz consistente.
    Todas las operaciones se delegan al folder remoto,
    pero los archivos se devuelven como CachedFile.
    """
    
    def __init__(self, remote_folder: Folder, cache_folder: Folder, origin: 'CachedOrigin'):
        """
        Inicializa la carpeta con caché.
        
        Args:
            remote_folder: Carpeta remota original
            cache_folder: Carpeta local que actúa como caché
            origin: CachedOrigin padre
        """
        super().__init__(origin, remote_folder.path)
        self._remote = remote_folder
        self._cache = cache_folder
        
        logger.debug(f"[tai-storage] CachedFolder inicializado: {remote_folder.path}")
    
    def exists(self) -> bool:
        """
        Verifica si la carpeta existe en el origen remoto.
        
        Returns:
            True si existe, False si no
        """
        return self._remote.exists()
    
    def create(self, parents: bool = True) -> None:
        """
        Crea la carpeta en el origen remoto y en caché.
        
        Args:
            parents: Si True, crea carpetas padres necesarias
        """
        logger.info(f"[tai-storage] Creando carpeta (remoto + caché): {self._remote.path}")
        
        # Crear en remoto
        self._remote.create(parents=parents)
        
        # Crear en caché
        self._cache.create(parents=parents)
    
    def delete(self, recursive: bool = False) -> None:
        """
        Elimina la carpeta del origen remoto y del caché.
        
        Args:
            recursive: Si True, elimina recursivamente todo el contenido
        """
        logger.warning(f"[tai-storage] Eliminando carpeta (remoto + caché): {self._remote.path} (recursive={recursive})")
        
        # Eliminar del remoto
        if self._remote.exists():
            self._remote.delete(recursive=recursive)
        
        # Eliminar del caché
        if self._cache.exists():
            self._cache.delete(recursive=recursive)
    
    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[CachedFile]:
        """
        Lista archivos en la carpeta remota.
        Devuelve CachedFile para cada archivo.
        
        Args:
            pattern: Patrón regex para filtrar nombres
            recursive: Si True, busca recursivamente en subdirectorios
            modified_after: Filtrar archivos modificados después de esta fecha
            modified_before: Filtrar archivos modificados antes de esta fecha
            created_after: Filtrar archivos creados después de esta fecha
            created_before: Filtrar archivos creados antes de esta fecha
            
        Returns:
            Lista de objetos CachedFile
        """
        logger.debug(f"[tai-storage] Listando archivos en: {self._remote.path} (recursive={recursive})")
        
        # Listar archivos del folder remoto
        remote_files = self._remote.list_files(
            pattern=pattern,
            recursive=recursive,
            modified_after=modified_after,
            modified_before=modified_before,
            created_after=created_after,
            created_before=created_before
        )
        
        # Convertir a CachedFile
        cached_files = []
        for remote_file in remote_files:
            # Calcular ruta relativa del archivo respecto a esta carpeta.
            # En modo recursivo, los archivos pueden estar en subcarpetas,
            # por lo que necesitamos preservar la estructura de directorios
            # en el caché (ej: "subfolder/file.parquet" en vez de solo "file.parquet").
            remote_folder_path = self._remote.path
            remote_file_dir = remote_file.folder.path

            if remote_file_dir == remote_folder_path:
                # Archivo en la misma carpeta
                relative_name = remote_file.filename
            else:
                try:
                    subfolder = remote_file_dir.relative_to(remote_folder_path)
                    relative_name = str(subfolder / remote_file.filename)
                except ValueError:
                    # Fallback
                    relative_name = remote_file.filename
            
            cache_file = self._cache.get_file(relative_name)
            cached_file = CachedFile(remote_file, cache_file, self)
            cached_files.append(cached_file)
        
        logger.debug(f"[tai-storage] Encontrados {len(cached_files)} archivos")
        return cached_files
    
    def list_folders(self, pattern: Optional[str] = None) -> List['CachedFolder']:
        """
        Lista subcarpetas.
        Devuelve CachedFolder para cada carpeta.
        
        Args:
            pattern: Patrón regex para filtrar nombres
            
        Returns:
            Lista de objetos CachedFolder
        """
        logger.debug(f"[tai-storage] Listando subcarpetas en: {self._remote.path}")
        
        # Listar carpetas remotas
        remote_folders = self._remote.list_folders(pattern=pattern)
        
        # Convertir a CachedFolder
        cached_folders = []
        for remote_folder in remote_folders:
            folder_name = remote_folder.path.name
            cache_folder = self.origin._cache_origin.get_folder(
                self._cache.path / folder_name
            )
            cached_folder = CachedFolder(remote_folder, cache_folder, self.origin)
            cached_folders.append(cached_folder)
        
        logger.debug(f"[tai-storage] Encontradas {len(cached_folders)} subcarpetas")
        return cached_folders
    
    def get_file(self, filename: Union[str, Path]) -> CachedFile:
        """
        Obtiene una referencia a un archivo como CachedFile.
        
        Args:
            filename: Nombre del archivo (puede ser ruta relativa)
            
        Returns:
            Objeto CachedFile
            
        Note:
            No verifica si el archivo existe, solo crea la referencia.
        """
        logger.debug(f"[tai-storage] Obteniendo archivo: {filename}")
        
        # Obtener archivos remoto y de caché
        remote_file = self._remote.get_file(filename)
        cache_file = self._cache.get_file(filename)
        
        return CachedFile(remote_file, cache_file, self)
    
    def get_folder(self, folder_name: Union[str, Path]) -> 'CachedFolder':
        """
        Obtiene una referencia a una subcarpeta como CachedFolder.

        Args:
            folder_name: Nombre de la subcarpeta (puede ser ruta relativa)

        Returns:
            Objeto CachedFolder

        Note:
            No verifica si la carpeta existe, solo crea la referencia.
        """
        logger.debug(f"[tai-storage] Obteniendo subcarpeta: {folder_name}")
        remote_subfolder = self._remote.get_folder(folder_name)
        cache_subfolder = self._cache.get_folder(folder_name)
        return CachedFolder(remote_subfolder, cache_subfolder, self.origin)

    def upload_file(
        self,
        source: Union[str, Path, bytes],
        destination: str,
        overwrite: bool = True
    ) -> CachedFile:
        """
        Sube un archivo al folder remoto y actualiza el caché.
        
        Args:
            source: Ruta del archivo local, Path o bytes a subir
            destination: Nombre del archivo destino
            overwrite: Si True, sobrescribe si existe
            
        Returns:
            Objeto CachedFile del archivo subido
        """
        logger.info(f"[tai-storage] Subiendo archivo a: {destination}")
        
        # Subir al remoto
        remote_file = self._remote.upload_file(source, destination, overwrite=overwrite)
        
        # Obtener el archivo de caché
        cache_file = self._cache.get_file(destination)
        
        # Actualizar caché
        if isinstance(source, bytes):
            cache_file.write(source)
        else:
            source_path = Path(source)
            with open(source_path, 'rb') as f:
                data = f.read()
            cache_file.write(data)
        
        logger.debug(f"[tai-storage] Archivo subido y cacheado: {destination}")
        
        return CachedFile(remote_file, cache_file, self)
    
    def delete_file(self, filename: str) -> None:
        """
        Elimina un archivo del folder remoto y del caché.
        
        Args:
            filename: Nombre del archivo a eliminar
        """
        logger.warning(f"[tai-storage] Eliminando archivo (remoto + caché): {filename}")
        
        # Eliminar del remoto
        self._remote.delete_file(filename)
        
        # Eliminar del caché
        cache_file = self._cache.get_file(filename)
        if cache_file.exists():
            cache_file.delete()
    
    def get_metadata(self) -> Dict[str, Any]:
        """
        Obtiene metadatos de la carpeta (delegando al remoto).
        
        Returns:
            Diccionario con metadatos
        """
        return self._remote.get_metadata()
    
    def __repr__(self) -> str:
        """Representación de la carpeta."""
        return f"CachedFolder({self._remote.path})"
