"""
Implementación de Folder para SharePoint usando Microsoft Graph API.
"""
from __future__ import annotations
from typing import List, Optional, Dict, Any, TYPE_CHECKING
from datetime import datetime
from pathlib import Path
import io

import requests

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

if TYPE_CHECKING:
    from .origin import SharePointOrigin
    from .file import SharePointFile

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


class SharePointFolder(Folder):
    """
    Representa una carpeta en SharePoint Online.
    
    Args:
        origin: SharePointOrigin asociado
        path: Ruta relativa desde la raíz del drive
    """
    
    def __init__(self, origin: SharePointOrigin, path: str):
        super().__init__(origin, path)
        self._item_id: Optional[str] = None
    
    def exists(self) -> bool:
        """Verifica si la carpeta existe en SharePoint."""
        try:
            self._get_item_id()
            return True
        except (FolderNotFoundError, requests.HTTPError):
            return False
    
    def _get_item_id(self) -> str:
        """Obtiene el item_id de esta carpeta desde Graph API."""
        if self._item_id:
            return self._item_id
        
        # Normalizar path
        path = self.path.strip('/')
        
        # Construir URL para Graph API
        if not path:
            # Raíz del drive
            url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/root"
        else:
            # Carpeta específica
            url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/root:/{path}"
        
        response = requests.get(url, headers=self.origin.headers)
        
        if response.status_code == 404:
            raise FolderNotFoundError(f"La carpeta '{self.path}' no existe")
        
        response.raise_for_status()
        data = response.json()
        
        # Verificar que es una carpeta
        if "folder" not in data:
            raise FolderNotFoundError(f"'{self.path}' no es una carpeta")
        
        self._item_id = data["id"]
        return self._item_id
    
    def create(self, parents: bool = True) -> None:
        """
        Crea la carpeta en SharePoint.
        
        Args:
            parents: Si True, crea carpetas padre necesarias
            
        Raises:
            FolderAlreadyExistsError: Si la carpeta ya existe
        """
        from ...exceptions import FolderAlreadyExistsError
        
        if self.exists():
            raise FolderAlreadyExistsError(f"La carpeta '{self.path}' ya existe")
        
        path = self.path.strip('/')
        parts = path.split('/')
        
        if parents:
            # Crear carpetas intermedias si no existen
            current_path = ""
            for part in parts:
                current_path = f"{current_path}/{part}" if current_path else part
                folder = SharePointFolder(self.origin, current_path)
                if not folder.exists():
                    folder._create()
        else:
            # Solo crear esta carpeta
            self._create()
    
    def _create(self) -> None:
        """Crea la carpeta en SharePoint (método interno)."""
        path = self.path.strip('/')
        parts = path.split('/')
        
        if len(parts) == 0:
            return  # La raíz ya existe
        
        folder_name = parts[-1]
        parent_path = '/'.join(parts[:-1]) if len(parts) > 1 else ''
        
        # Obtener ID del padre
        if parent_path:
            from .folder import SharePointFolder
            parent_folder = SharePointFolder(self.origin, parent_path)
            parent_id = parent_folder._get_item_id()
        else:
            parent_id = "root"
        
        # Crear carpeta
        url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/items/{parent_id}/children"
        
        payload = {
            "name": folder_name,
            "folder": {},
            "@microsoft.graph.conflictBehavior": "fail"
        }
        
        response = requests.post(url, headers=self.origin.headers, json=payload)
        
        if response.status_code == 409:
            # Ya existe
            logger.debug(f"La carpeta '{self.path}' ya existe")
            return
        
        response.raise_for_status()
        self._item_id = response.json()["id"]
        logger.info(f"✓ Carpeta creada: {self.path}")
    
    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['SharePointFile']:
        """
        Lista archivos como objetos SharePointFile con filtrado opcional.
        
        Args:
            pattern: Patrón regex para filtrar nombres
            recursive: Si True, busca recursivamente
            modified_after: Solo archivos modificados después de esta fecha
            modified_before: Solo archivos modificados antes de esta fecha
            created_after: Solo archivos creados después de esta fecha
            created_before: Solo archivos creados antes de esta fecha
            
        Returns:
            Lista de objetos SharePointFile que cumplen los criterios
        """
        import re
        from .file import SharePointFile
        
        files = []
        
        # Obtener items de la carpeta
        item_id = self._get_item_id()
        url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/items/{item_id}/children"
        
        response = requests.get(url, headers=self.origin.headers)
        response.raise_for_status()
        
        items = response.json().get("value", [])
        
        for item in items:
            if "file" in item:
                # Es un archivo
                filename = item["name"]
                
                # Filtrar por patrón
                if pattern and not re.search(pattern, filename, re.IGNORECASE):
                    continue
                
                # Parsear fechas para filtros
                modified_time = datetime.fromisoformat(item["lastModifiedDateTime"].replace('Z', '+00:00'))
                created_time = datetime.fromisoformat(item["createdDateTime"].replace('Z', '+00:00'))
                
                # 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 con path relativo
                # Construir el path relativo del archivo (carpeta + nombre)
                if self.path:
                    file_path = f"{self.path}/{filename}"
                else:
                    file_path = filename
                
                file = SharePointFile(self, filename, file_path)
                files.append(file)
            
            elif recursive and "folder" in item:
                # Es una carpeta y queremos recursión
                subfolder_path = f"{self.path}/{item['name']}" if self.path else item['name']
                subfolder = SharePointFolder(self.origin, subfolder_path)
                subfiles = subfolder.list_files(
                    pattern=pattern,
                    recursive=True,
                    modified_after=modified_after,
                    modified_before=modified_before,
                    created_after=created_after,
                    created_before=created_before
                )
                files.extend(subfiles)
        
        logger.debug(f"Encontrados {len(files)} archivos en {self.path}")
        return files
    
    def list_folders(self, pattern: Optional[str] = None) -> List['SharePointFolder']:
        """
        Lista las subcarpetas como objetos SharePointFolder.
        
        Args:
            pattern: Patrón regex opcional para filtrar nombres
            
        Returns:
            Lista de objetos SharePointFolder
        """
        import re
        
        folders = []
        
        item_id = self._get_item_id()
        url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/items/{item_id}/children"
        
        response = requests.get(url, headers=self.origin.headers)
        response.raise_for_status()
        
        items = response.json().get("value", [])
        
        for item in items:
            if "folder" in item:
                folder_name = item["name"]
                
                if pattern and not re.search(pattern, folder_name, re.IGNORECASE):
                    continue
                
                # Crear objeto Folder
                subfolder_path = f"{self.path}/{folder_name}" if self.path else folder_name
                folder = SharePointFolder(self.origin, subfolder_path)
                folders.append(folder)
        
        logger.debug(f"Encontradas {len(folders)} carpetas en {self.path}")
        return folders
    
    def get_file(self, filename: str) -> SharePointFile:
        """
        Obtiene una referencia a un archivo.
        
        Args:
            filename: Nombre del archivo
            
        Returns:
            SharePointFile
        """
        from .file import SharePointFile
        
        # Construir ruta completa
        file_path = f"{self.path}/{filename}" if self.path else filename
        
        file = SharePointFile(self, filename, file_path)
        
        if not file.exists():
            raise FileNotFoundError(f"El archivo '{filename}' no existe en '{self.path}'")
        
        return file
    
    def upload_file(
        self,
        source: Any,
        destination: str,
        overwrite: bool = False
    ) -> SharePointFile:
        """
        Sube un archivo a la carpeta.
        
        Args:
            source: Puede ser Path, str (ruta), bytes, o file-like object
            destination: Nombre del archivo destino
            overwrite: Si True, sobrescribe si existe
            
        Returns:
            SharePointFile subido
        """
        from .file import SharePointFile
        
        # Convertir source a bytes
        if isinstance(source, (str, Path)):
            with open(source, 'rb') as f:
                content = f.read()
        elif isinstance(source, bytes):
            content = source
        elif hasattr(source, 'read'):
            content = source.read()
            if isinstance(content, str):
                content = content.encode('utf-8')
        else:
            raise UploadError(f"Tipo de source no soportado: {type(source)}")
        
        # Construir ruta completa del archivo
        file_path = f"{self.path}/{destination}" if self.path else destination
        
        # URL para upload
        url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/root:/{file_path}:/content"
        
        headers = self.origin.headers.copy()
        headers["Content-Type"] = "application/octet-stream"
        
        # Ajustar conflictBehavior
        conflict_behavior = "replace" if overwrite else "fail"
        url += f"?@microsoft.graph.conflictBehavior={conflict_behavior}"
        
        response = requests.put(url, headers=headers, data=content)
        
        if response.status_code == 409:
            raise UploadError(f"El archivo '{destination}' ya existe. Usa overwrite=True para reemplazar.")
        
        response.raise_for_status()
        
        logger.info(f"✓ Archivo subido: {destination} a {self.path}")
        
        return SharePointFile(self, destination, file_path)
    
    def delete_file(self, filename: str) -> None:
        """
        Elimina un archivo de la carpeta.
        
        Args:
            filename: Nombre del archivo a eliminar
        """
        file = self.get_file(filename)
        file.delete()
    
    def delete(self, recursive: bool = False) -> None:
        """
        Elimina la carpeta de SharePoint.
        
        Nota: En SharePoint Graph API, eliminar una carpeta siempre elimina
        todo su contenido, por lo que el parámetro recursive se ignora.
        
        Args:
            recursive: Ignorado (Graph API siempre elimina recursivamente)
            
        Raises:
            FolderNotFoundError: Si la carpeta no existe
        """
        if not self.exists():
            raise FolderNotFoundError(f"La carpeta '{self.path}' no existe")
        
        # Obtener el item ID de la carpeta
        item_id = self._get_item_id()
        
        # Eliminar la carpeta usando Graph API
        url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/items/{item_id}"
        
        response = requests.delete(url, headers=self.origin.headers)
        response.raise_for_status()
        
        logger.info(f"✓ Carpeta eliminada: {self.path}")
    
    def get_metadata(self) -> Dict[str, Any]:
        """
        Obtiene metadatos de la carpeta desde SharePoint.
        
        Returns:
            Diccionario con metadatos de la carpeta:
            {
                'path': 'ruta/carpeta',
                'name': 'nombre_carpeta',
                'created_time': datetime(...),
                'modified_time': datetime(...),
                'item_id': 'id_del_item',
                'web_url': 'https://...',
                'child_count': 10
            }
            
        Raises:
            FolderNotFoundError: Si la carpeta no existe
        """
        if not self.exists():
            raise FolderNotFoundError(f"La carpeta '{self.path}' no existe")
        
        item_id = self._get_item_id()
        
        # Obtener información completa del item
        url = f"https://graph.microsoft.com/v1.0/drives/{self.origin.drive_id}/items/{item_id}"
        response = requests.get(url, headers=self.origin.headers)
        response.raise_for_status()
        
        data = response.json()
        
        # Parsear fechas
        created_time = datetime.fromisoformat(data["createdDateTime"].replace('Z', '+00:00'))
        modified_time = datetime.fromisoformat(data["lastModifiedDateTime"].replace('Z', '+00:00'))
        
        metadata = {
            'path': self.path,
            'name': data.get('name', ''),
            'created_time': created_time,
            'modified_time': modified_time,
            'item_id': item_id,
            'web_url': data.get('webUrl', ''),
            'child_count': data.get('folder', {}).get('childCount', 0)
        }
        
        logger.debug(f"Metadata obtenida para carpeta: {self.path}")
        return metadata
