import re
import os
from datetime import datetime
import pytz
from werkzeug.utils import secure_filename
from flask import current_app

def validate_email(email):
    """
    Validar formato de dirección de correo electrónico.
    
    Args:
        email (str): Dirección de correo a validar
        
    Returns:
        bool: True si es válido, False en caso contrario
    """
    # Patrón básico para validar emails
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

def validate_username(username):
    """
    Validar formato de nombre de usuario.
    
    Args:
        username (str): Nombre de usuario a validar
        
    Returns:
        bool: True si es válido, False en caso contrario
    """
    # Solo letras, números, guiones y guiones bajos, longitud entre 3 y 20
    pattern = r'^[a-zA-Z0-9_-]{3,20}$'
    return bool(re.match(pattern, username))

def validate_password_strength(password):
    """
    Validar fortaleza de contraseña.
    
    Args:
        password (str): Contraseña a validar
        
    Returns:
        tuple: (is_valid, message)
            - is_valid (bool): True si es válida
            - message (str): Mensaje descriptivo si no es válida
    """
    # Verificar longitud mínima
    if len(password) < 8:
        return False, "La contraseña debe tener al menos 8 caracteres."
    
    # Verificar presencia de letras
    if not re.search(r'[a-zA-Z]', password):
        return False, "La contraseña debe contener al menos una letra."
    
    # Verificar presencia de números
    if not re.search(r'\d', password):
        return False, "La contraseña debe contener al menos un número."
    
    # Verificar presencia de caracteres especiales (opcional)
    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False, "La contraseña debe contener al menos un carácter especial."
    
    return True, "Contraseña válida."

def validate_date_format(date_str, format='%Y-%m-%d'):
    """
    Validar formato de fecha.
    
    Args:
        date_str (str): Fecha en formato string
        format (str, optional): Formato esperado. Por defecto '%Y-%m-%d'.
        
    Returns:
        bool: True si es válido, False en caso contrario
    """
    try:
        datetime.strptime(date_str, format)
        return True
    except ValueError:
        return False

def validate_iso_date(date_str):
    """
    Validar fecha en formato ISO 8601.
    
    Args:
        date_str (str): Fecha en formato ISO 8601
        
    Returns:
        bool: True si es válido, False en caso contrario
    """
    try:
        datetime.fromisoformat(date_str.replace('Z', '+00:00'))
        return True
    except (ValueError, AttributeError):
        return False

def validate_file_extension(filename, allowed_extensions):
    """
    Validar extensión de archivo.
    
    Args:
        filename (str): Nombre del archivo
        allowed_extensions (list): Lista de extensiones permitidas
        
    Returns:
        bool: True si es válido, False en caso contrario
    """
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in allowed_extensions

def validate_file_size(file, max_size_bytes):
    """
    Validar tamaño de archivo.
    
    Args:
        file: Objeto de archivo (de Flask)
        max_size_bytes (int): Tamaño máximo en bytes
        
    Returns:
        bool: True si es válido, False en caso contrario
    """
    # Guardar posición actual
    current_position = file.tell()
    
    # Ir al final para obtener el tamaño
    file.seek(0, os.SEEK_END)
    size = file.tell()
    
    # Restaurar posición
    file.seek(current_position)
    
    return size <= max_size_bytes

def validate_project_data(data):
    """
    Validar datos de proyecto.
    
    Args:
        data (dict): Datos del proyecto a validar
        
    Returns:
        tuple: (is_valid, errors)
            - is_valid (bool): True si es válido
            - errors (dict): Diccionario con errores por campo
    """
    errors = {}
    
    # Validar campos obligatorios
    required_fields = ['descripcion', 'cliente', 'esquema']
    for field in required_fields:
        if field not in data or not data[field]:
            errors[field] = f"El campo {field} es obligatorio."
    
    # Validar longitud de descripción
    if 'descripcion' in data and data['descripcion']:
        if len(data['descripcion']) < 5:
            errors['descripcion'] = "La descripción debe tener al menos 5 caracteres."
        elif len(data['descripcion']) > 100:
            errors['descripcion'] = "La descripción no puede exceder los 100 caracteres."
    
    # Validar esquema
    if 'esquema' in data and data['esquema']:
        from services.schema_service import get_schema
        schema = get_schema(data['esquema'])
        if not schema:
            errors['esquema'] = "El esquema seleccionado no existe."
    
    # Validar proyecto padre si se especifica
    if 'proyecto_padre' in data and data['proyecto_padre']:
        from services.project_service import get_project
        parent_id = data['proyecto_padre'].replace('PROJ', '')
        try:
            parent_id = int(parent_id)
            parent = get_project(parent_id)
            if not parent:
                errors['proyecto_padre'] = "El proyecto padre seleccionado no existe."
        except ValueError:
            errors['proyecto_padre'] = "Formato de ID de proyecto padre inválido."
    
    return len(errors) == 0, errors

def validate_document_data(data, file):
    """
    Validar datos de documento.
    
    Args:
        data (dict): Datos del documento a validar
        file: Objeto de archivo (de Flask)
        
    Returns:
        tuple: (is_valid, errors)
            - is_valid (bool): True si es válido
            - errors (dict): Diccionario con errores por campo
    """
    errors = {}
    
    # Validar campos obligatorios
    if 'nombre' not in data or not data['nombre']:
        errors['nombre'] = "El nombre del documento es obligatorio."
    
    # Validar archivo
    if not file:
        errors['file'] = "Debe seleccionar un archivo."
    else:
        # Validar extensión
        filename = secure_filename(file.filename)
        extension = filename.rsplit('.', 1)[1].lower() if '.' in filename else ''
        
        from services.document_service import get_allowed_filetypes
        allowed_filetypes = get_allowed_filetypes()
        
        if extension not in allowed_filetypes:
            errors['file'] = f"Tipo de archivo no permitido: {extension}"
        else:
            # Validar tamaño
            max_size = allowed_filetypes[extension].get('tamano_maximo', 10485760)  # 10MB por defecto
            
            if not validate_file_size(file, max_size):
                errors['file'] = f"El archivo excede el tamaño máximo permitido ({max_size // 1048576} MB)."
    
    return len(errors) == 0, errors

def validate_schema_data(data):
    """
    Validar datos de esquema.
    
    Args:
        data (dict): Datos del esquema a validar
        
    Returns:
        tuple: (is_valid, errors)
            - is_valid (bool): True si es válido
            - errors (dict): Diccionario con errores por campo
    """
    errors = {}
    
    # Validar campos obligatorios
    if 'descripcion' not in data or not data['descripcion']:
        errors['descripcion'] = "La descripción del esquema es obligatoria."
    
    if 'documentos' not in data or not data['documentos']:
        errors['documentos'] = "Se requiere al menos un tipo de documento en el esquema."
    
    # Validar documentos
    if 'documentos' in data and data['documentos']:
        for i, doc in enumerate(data['documentos']):
            # Validar campos obligatorios de cada documento
            if 'tipo' not in doc or not doc['tipo']:
                errors[f'documentos[{i}].tipo'] = "El tipo de documento es obligatorio."
            
            if 'nombre' not in doc or not doc['nombre']:
                errors[f'documentos[{i}].nombre'] = "El nombre del documento es obligatorio."
            
            # Validar tipo de documento
            if 'tipo' in doc and doc['tipo']:
                from services.document_service import get_allowed_filetypes
                allowed_filetypes = get_allowed_filetypes()
                
                if doc['tipo'] not in allowed_filetypes:
                    errors[f'documentos[{i}].tipo'] = f"Tipo de documento no permitido: {doc['tipo']}"
            
            # Validar niveles
            if 'nivel_ver' in doc and not isinstance(doc['nivel_ver'], int):
                errors[f'documentos[{i}].nivel_ver'] = "El nivel de visualización debe ser un número entero."
            
            if 'nivel_editar' in doc and not isinstance(doc['nivel_editar'], int):
                errors[f'documentos[{i}].nivel_editar'] = "El nivel de edición debe ser un número entero."
    
    return len(errors) == 0, errors

def validate_user_data(data, is_new_user=True):
    """
    Validar datos de usuario.
    
    Args:
        data (dict): Datos del usuario a validar
        is_new_user (bool, optional): Indica si es un nuevo usuario.
            Por defecto es True.
            
    Returns:
        tuple: (is_valid, errors)
            - is_valid (bool): True si es válido
            - errors (dict): Diccionario con errores por campo
    """
    errors = {}
    
    # Validar campos obligatorios para nuevos usuarios
    if is_new_user:
        required_fields = ['nombre', 'username', 'email', 'password']
        for field in required_fields:
            if field not in data or not data[field]:
                errors[field] = f"El campo {field} es obligatorio."
    
    # Validar username
    if 'username' in data and data['username']:
        if not validate_username(data['username']):
            errors['username'] = "El nombre de usuario debe contener solo letras, números, guiones y guiones bajos, y tener entre 3 y 20 caracteres."
        elif is_new_user:
            # Verificar disponibilidad solo para nuevos usuarios
            from services.user_service import check_username_availability
            if not check_username_availability(data['username']):
                errors['username'] = "El nombre de usuario ya está en uso."
    
    # Validar email
    if 'email' in data and data['email']:
        if not validate_email(data['email']):
            errors['email'] = "El formato de correo electrónico no es válido."
    
    # Validar contraseña para nuevos usuarios o si se proporciona
    if is_new_user or ('password' in data and data['password']):
        if 'password' in data and data['password']:
            is_valid, message = validate_password_strength(data['password'])
            if not is_valid:
                errors['password'] = message
    
    # Validar nivel
    if 'nivel' in data:
        try:
            nivel = int(data['nivel'])
            if nivel < 0 or nivel > 9999:
                errors['nivel'] = "El nivel debe estar entre 0 y 9999."
        except (ValueError, TypeError):
            errors['nivel'] = "El nivel debe ser un número entero."
    
    # Validar fecha de caducidad
    if 'fecha_caducidad' in data and data['fecha_caducidad']:
        if not validate_iso_date(data['fecha_caducidad']):
            errors['fecha_caducidad'] = "El formato de fecha de caducidad no es válido. Use formato ISO 8601 (YYYY-MM-DDTHH:MM:SSZ)."
    
    return len(errors) == 0, errors
