"""
Implementación de Origin para SharePoint usando Microsoft Graph API.
"""
from __future__ import annotations
from typing import List, Optional, TYPE_CHECKING
from pathlib import Path

from msal import ConfidentialClientApplication
import requests

from tai_alphi import Alphi
from ...base.origin import Origin
from ...exceptions import OriginConnectionError, AuthenticationError, FolderNotFoundError

if TYPE_CHECKING:
    from .folder import SharePointFolder

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


class SharePointOrigin(Origin):
    """
    Origin para SharePoint Online usando Microsoft Graph API.
    
    Usa autenticación con Azure AD (client_id + client_secret) y accede a SharePoint
    a través de Microsoft Graph API.
    
    Args:
        tenant_id: ID del tenant de Azure AD (o dominio.onmicrosoft.com)
        client_id: Application (client) ID registrado en Azure AD
        client_secret: Client secret de la aplicación
        site_name: Nombre del sitio de SharePoint (sin https://)
        hostname: Hostname de SharePoint (ej: contoso.sharepoint.com)
    """
    
    def __init__(
        self,
        tenant_id: str,
        client_id: str,
        client_secret: str,
        site_name: str,
        hostname: str
    ):
        self.tenant_id = tenant_id
        self.client_id = client_id
        self.client_secret = client_secret
        self.site_name = site_name
        self.hostname = hostname
        
        self._msal_app: Optional[ConfidentialClientApplication] = None
        self._access_token: Optional[str] = None
        self._site_id: Optional[str] = None
        self._drive_id: Optional[str] = None
        
        logger.info(f"SharePointOrigin inicializado para {hostname}/{site_name}")
    
    def connect(self) -> None:
        """Establece conexión con SharePoint obteniendo token de acceso."""
        try:
            # Crear aplicación MSAL
            authority = f"https://login.microsoftonline.com/{self.tenant_id}"
            self._msal_app = ConfidentialClientApplication(
                client_id=self.client_id,
                client_credential=self.client_secret,
                authority=authority
            )
            
            # Obtener token
            scopes = ["https://graph.microsoft.com/.default"]
            result = self._msal_app.acquire_token_for_client(scopes=scopes)
            
            if "access_token" not in result:
                error = result.get("error_description", "Unknown error")
                raise AuthenticationError(f"No se pudo obtener token: {error}")
            
            self._access_token = result["access_token"]
            
            # Obtener site_id y drive_id
            self._get_site_info()
            
            logger.info(f"✓ Conectado a SharePoint {self.hostname}/{self.site_name}")
            
        except Exception as e:
            logger.error(f"Error conectando a SharePoint: {e}")
            raise OriginConnectionError(f"Error de conexión a SharePoint: {e}")
    
    def disconnect(self) -> None:
        """Cierra la conexión con SharePoint."""
        self._access_token = None
        self._site_id = None
        self._drive_id = None
        logger.info("Desconectado de SharePoint")
    
    def is_connected(self) -> bool:
        """Verifica si hay conexión activa."""
        return self._access_token is not None
    
    def _get_site_info(self) -> None:
        """Obtiene el site_id y drive_id del sitio de SharePoint."""
        headers = {"Authorization": f"Bearer {self._access_token}"}
        
        # Obtener site ID
        url = f"https://graph.microsoft.com/v1.0/sites/{self.hostname}:/sites/{self.site_name}"
        response = requests.get(url, headers=headers)
        
        if response.status_code != 200:
            raise OriginConnectionError(f"No se encontró el sitio: {response.text}")
        
        self._site_id = response.json()["id"]
        
        # Obtener drive por defecto (Documents)
        url = f"https://graph.microsoft.com/v1.0/sites/{self._site_id}/drive"
        response = requests.get(url, headers=headers)
        
        if response.status_code != 200:
            raise OriginConnectionError(f"No se encontró el drive: {response.text}")
        
        self._drive_id = response.json()["id"]
        
        logger.debug(f"Site ID: {self._site_id}, Drive ID: {self._drive_id}")
    
    def get_folder(self, path: str) -> SharePointFolder:
        """
        Obtiene una referencia a una carpeta.
        
        Args:
            path: Ruta de la carpeta (relativa a la raíz del drive)
            
        Returns:
            SharePointFolder configurado
        """
        if not self.is_connected():
            self.connect()
        
        from .folder import SharePointFolder
        
        folder = SharePointFolder(self, path)
        
        # Verificar que existe
        if not folder.exists():
            raise FolderNotFoundError(f"La carpeta '{path}' no existe")
        
        logger.debug(f"Obtenida carpeta: {path}")
        return folder
    
    def create_folder(self, path: str, parents: bool = True) -> SharePointFolder:
        """
        Crea una carpeta en SharePoint.
        
        Args:
            path: Ruta de la carpeta a crear
            parents: Si True, crea carpetas padre necesarias
            
        Returns:
            SharePointFolder creado
        """
        if not self.is_connected():
            self.connect()
        
        from .folder import SharePointFolder
        
        # Normalizar path
        path = path.strip('/')
        parts = path.split('/')
        
        if parents:
            # Crear carpetas intermedias
            current_path = ""
            for part in parts:
                current_path = f"{current_path}/{part}" if current_path else part
                folder = SharePointFolder(self, current_path)
                if not folder.exists():
                    folder._create()
        else:
            # Crear solo la carpeta final
            folder = SharePointFolder(self, path)
            if not folder.exists():
                folder._create()
        
        logger.info(f"✓ Carpeta creada: {path}")
        return SharePointFolder(self, path)
    
    def folder_exists(self, path: str) -> bool:
        """
        Verifica si existe una carpeta.
        
        Args:
            path: Ruta de la carpeta
            
        Returns:
            True si existe, False si no
        """
        if not self.is_connected():
            self.connect()
        
        from .folder import SharePointFolder
        
        folder = SharePointFolder(self, path)
        return folder.exists()
    
    def list_folders(self, path: str = "", pattern: Optional[str] = None) -> List['SharePointFolder']:
        """
        Lista las carpetas en una ruta como objetos SharePointFolder.
        
        Args:
            path: Ruta donde buscar carpetas (vacío = raíz)
            pattern: Patrón regex opcional para filtrar nombres
            
        Returns:
            Lista de objetos SharePointFolder
        """
        if not self.is_connected():
            self.connect()
        
        from .folder import SharePointFolder
        
        folder = SharePointFolder(self, path)
        return folder.list_folders(pattern=pattern)
    
    def delete_folder(self, path: str, recursive: bool = False) -> None:
        """
        Elimina una 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:
            path: Ruta de la carpeta a eliminar
            recursive: Ignorado (Graph API siempre elimina recursivamente)
            
        Raises:
            FolderNotFoundError: Si la carpeta no existe
        """
        if not self.is_connected():
            self.connect()
        
        from .folder import SharePointFolder
        
        folder = SharePointFolder(self, path)
        folder.delete(recursive=recursive)
    
    @property
    def headers(self) -> dict:
        """Headers HTTP con autenticación para Graph API."""
        if not self._access_token:
            raise OriginConnectionError("No hay token de acceso. Llama a connect() primero.")
        return {
            "Authorization": f"Bearer {self._access_token}",
            "Content-Type": "application/json"
        }
    
    @property
    def site_id(self) -> str:
        """ID del sitio de SharePoint."""
        if not self._site_id:
            raise OriginConnectionError("No conectado. Llama a connect() primero.")
        return self._site_id
    
    @property
    def drive_id(self) -> str:
        """ID del drive de SharePoint."""
        if not self._drive_id:
            raise OriginConnectionError("No conectado. Llama a connect() primero.")
        return self._drive_id
