"""
File Utilities
Utilidades para verificación de archivos y espacio en disco
"""

import os
import shutil
import psutil
from pathlib import Path
from typing import Tuple, List, Optional, Dict
import logging


class FileChecker:
    """Verificador de acceso a archivos y detección de archivos en uso"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
    
    def check_file_accessibility(self, file_path: str) -> Tuple[bool, str]:
        """
        Verificar si un archivo es accesible para lectura
        Retorna: (es_accesible, mensaje_error)
        """
        try:
            path = Path(file_path)
            if not path.exists():
                return False, f"Archivo no existe: {file_path}"
            
            # Intentar abrir el archivo en modo lectura
            with open(file_path, 'rb') as f:
                # Leer un byte para verificar acceso real
                f.read(1)
            
            return True, ""
            
        except PermissionError:
            return False, f"Sin permisos para acceder: {file_path}"
        except OSError as e:
            if "being used by another process" in str(e).lower():
                return False, f"Archivo en uso: {file_path}"
            return False, f"Error de sistema: {e}"
        except Exception as e:
            return False, f"Error inesperado: {e}"
    
    def check_directory_accessibility(self, directory_path: str, 
                                    check_all_files: bool = False) -> Tuple[bool, List[str]]:
        """
        Verificar accesibilidad de un directorio
        Si check_all_files es True, verifica todos los archivos
        Retorna: (es_accesible, lista_errores)
        """
        try:
            path = Path(directory_path)
            if not path.exists():
                return False, [f"Directorio no existe: {directory_path}"]
            
            if not path.is_dir():
                return False, [f"La ruta no es un directorio: {directory_path}"]
            
            errors = []
            
            if check_all_files:
                # Verificar todos los archivos en el directorio
                for file_path in path.rglob("*"):
                    if file_path.is_file():
                        accessible, error_msg = self.check_file_accessibility(str(file_path))
                        if not accessible:
                            errors.append(error_msg)
            else:
                # Solo verificar acceso básico al directorio
                try:
                    list(path.iterdir())
                except PermissionError:
                    errors.append(f"Sin permisos para acceder al directorio: {directory_path}")
                except Exception as e:
                    errors.append(f"Error accediendo al directorio: {e}")
            
            return len(errors) == 0, errors
            
        except Exception as e:
            return False, [f"Error verificando directorio {directory_path}: {e}"]
    
    def check_s7p_file_exclusively(self, s7p_file_path: str) -> Tuple[bool, str]:
        """
        Verificar específicamente el archivo .s7p para exclusividad
        Método rápido antes de procesar todo el proyecto
        """
        try:
            # Primero verificar acceso básico
            accessible, error_msg = self.check_file_accessibility(s7p_file_path)
            if not accessible:
                return False, error_msg
            
            # Intentar crear un backup temporal para verificar exclusividad
            import tempfile
            temp_dir = tempfile.gettempdir()
            temp_file = Path(temp_dir) / f"autobackup_test_{Path(s7p_file_path).name}"
            
            try:
                shutil.copy2(s7p_file_path, temp_file)
                # Si la copia fue exitosa, el archivo no está en uso exclusivo
                temp_file.unlink()  # Eliminar archivo temporal
                return True, ""
            except Exception as e:
                if "being used by another process" in str(e).lower():
                    return False, f"Archivo S7P en uso exclusivo: {s7p_file_path}"
                return False, f"Error verificando exclusividad S7P: {e}"
                
        except Exception as e:
            return False, f"Error verificando archivo S7P {s7p_file_path}: {e}"
    
    def is_file_locked(self, file_path: str) -> bool:
        """
        Verificar si un archivo específico está bloqueado
        """
        accessible, _ = self.check_file_accessibility(file_path)
        return not accessible
    
    def get_locked_files_in_directory(self, directory_path: str) -> List[str]:
        """
        Obtener lista de archivos bloqueados en un directorio
        """
        locked_files = []
        try:
            path = Path(directory_path)
            for file_path in path.rglob("*"):
                if file_path.is_file():
                    if self.is_file_locked(str(file_path)):
                        locked_files.append(str(file_path))
        except Exception as e:
            self.logger.error(f"Error buscando archivos bloqueados en {directory_path}: {e}")
        
        return locked_files


class DiskSpaceChecker:
    """Verificador de espacio en disco"""
    
    def __init__(self):
        self.logger = logging.getLogger(__name__)
    
    def get_free_space_mb(self, path: str) -> float:
        """
        Obtener espacio libre en MB para una ruta específica
        """
        try:
            if os.name == 'nt':  # Windows
                free_bytes = shutil.disk_usage(path).free
            else:  # Linux/Mac
                statvfs = os.statvfs(path)
                free_bytes = statvfs.f_frsize * statvfs.f_bavail
            
            return free_bytes / (1024 * 1024)  # Convertir a MB
            
        except Exception as e:
            self.logger.error(f"Error obteniendo espacio libre para {path}: {e}")
            return 0.0
    
    def get_disk_usage_info(self, path: str) -> Tuple[float, float, float]:
        """
        Obtener información completa de uso de disco
        Retorna: (total_mb, usado_mb, libre_mb)
        """
        try:
            if os.name == 'nt':  # Windows
                usage = shutil.disk_usage(path)
                total_mb = usage.total / (1024 * 1024)
                free_mb = usage.free / (1024 * 1024)
                used_mb = total_mb - free_mb
            else:  # Linux/Mac
                statvfs = os.statvfs(path)
                total_mb = (statvfs.f_frsize * statvfs.f_blocks) / (1024 * 1024)
                free_mb = (statvfs.f_frsize * statvfs.f_bavail) / (1024 * 1024)
                used_mb = total_mb - free_mb
            
            return total_mb, used_mb, free_mb
            
        except Exception as e:
            self.logger.error(f"Error obteniendo información de disco para {path}: {e}")
            return 0.0, 0.0, 0.0
    
    def has_sufficient_space(self, path: str, required_mb: float, 
                           min_free_mb: float = 100.0) -> Tuple[bool, float]:
        """
        Verificar si hay suficiente espacio para una operación
        Retorna: (tiene_espacio_suficiente, espacio_libre_actual_mb)
        """
        try:
            free_mb = self.get_free_space_mb(path)
            total_required = required_mb + min_free_mb
            
            return free_mb >= total_required, free_mb
            
        except Exception as e:
            self.logger.error(f"Error verificando espacio suficiente para {path}: {e}")
            return False, 0.0
    
    def estimate_directory_size_mb(self, directory_path: str) -> float:
        """
        Estimar el tamaño de un directorio en MB
        """
        try:
            total_size = 0
            path = Path(directory_path)
            
            for file_path in path.rglob("*"):
                if file_path.is_file():
                    try:
                        total_size += file_path.stat().st_size
                    except (OSError, PermissionError):
                        # Saltar archivos inaccesibles
                        continue
            
            return total_size / (1024 * 1024)  # Convertir a MB
            
        except Exception as e:
            self.logger.error(f"Error estimando tamaño de {directory_path}: {e}")
            return 0.0
    
    def get_system_disk_info(self) -> Dict[str, Tuple[float, float, float]]:
        """
        Obtener información de todos los discos del sistema
        Retorna: {drive: (total_mb, usado_mb, libre_mb)}
        """
        disk_info = {}
        try:
            # Obtener todas las particiones
            partitions = psutil.disk_partitions()
            
            for partition in partitions:
                try:
                    usage = psutil.disk_usage(partition.mountpoint)
                    total_mb = usage.total / (1024 * 1024)
                    used_mb = usage.used / (1024 * 1024)
                    free_mb = usage.free / (1024 * 1024)
                    
                    disk_info[partition.device] = (total_mb, used_mb, free_mb)
                    
                except PermissionError:
                    # Saltar unidades sin acceso
                    continue
                    
        except Exception as e:
            self.logger.error(f"Error obteniendo información del sistema: {e}")
        
        return disk_info
