import re from typing import Any, Optional, Tuple # Regex práctico (no RFC completo) para validar emails _EMAIL_RE = re.compile( r"""^(?=.{3,254}$) # longitud total razonable (?=.{1,64}@) # parte local máx. 64 [A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+ (?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)* # puntos en la parte local @ (?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+ # labels dominio [A-Za-z]{2,63}$ # TLD """, re.X, ) def corregir_y_validar_email(texto: Any) -> Tuple[bool, Optional[str]]: """ Normaliza y valida un email. Correcciones aplicadas: - Reemplaza comas por puntos. - Elimina espacios alrededor y dentro (p. ej. 'a @ b . com' -> 'a@b.com'). - Convierte el dominio a minúsculas. - Colapsa puntos consecutivos en el dominio ('..' -> '.'). Devuelve (es_valido, email_corregido | None). """ if texto is None: return False, None s = str(texto).strip() # 1) comas -> puntos s = s.replace(",", ".") # 2) quita espacios s = re.sub(r"\s+", "", s) # 3) separar local y dominio (si es posible) if s.count("@") != 1: # no intentamos correcciones más agresivas si hay 0 o >1 '@' return False, s or None local, domain = s.split("@", 1) # 4) normalizaciones de dominio domain = domain.lower() domain = re.sub(r"\.{2,}", ".", domain) # colapsar puntos dobles candidato = f"{local}@{domain}" # 5) validar es_valido = _EMAIL_RE.match(candidato) is not None return es_valido, (candidato if candidato else None)