---
name: imap_move_message
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def imap_move_message(conn, uid: int, dest_mailbox: str) -> dict"
description: "Mueve un mensaje IMAP (por UID) del mailbox seleccionado a dest_mailbox sobre una conexion imaplib.IMAP4_SSL ya autenticada. Intenta primero conn.uid('MOVE', str(uid), dest_mailbox) (RFC 6851, atomico, soportado por Gmail) detectando la capability en conn.capabilities; si el servidor NO anuncia MOVE o el comando falla, usa el fallback clasico equivalente: UID COPY a dest + UID STORE +FLAGS (\\Deleted) en origen + EXPUNGE. Devuelve el camino usado en method ('move' o 'copy_delete'). Opera siempre por UID (estable dentro del mailbox), nunca por numero de secuencia. No abre la conexion ni resuelve credenciales: el caller pasa conn ya conectado, autenticado y con conn.select() del mailbox origen hecho. Nunca lanza: devuelve {status:'ok', uid, dest, method} o {status:'error', error}. En Gmail los nombres de carpeta llevan el prefijo '[Gmail]/' y mover a '[Gmail]/Trash' = papelera. Parte del grupo email/imap. Solo stdlib (imaplib)."
tags: [email, imap, mail, move, mailbox, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_py_core"
imports: [imaplib]
params:
  - name: conn
    desc: "objeto imaplib.IMAP4_SSL (o IMAP4) YA conectado, autenticado y con el mailbox ORIGEN seleccionado (conn.select('INBOX')). Normalmente lo produce imap_connect. La funcion no lo abre ni lo cierra. Lee conn.capabilities para decidir MOVE vs fallback."
  - name: uid
    desc: "UID del mensaje en el mailbox origen. Operacion siempre por UID (estable mientras no cambie la UIDVALIDITY), nunca por numero de secuencia."
  - name: dest_mailbox
    desc: "nombre del mailbox destino. En Gmail lleva el prefijo '[Gmail]/' (ej. '[Gmail]/Trash' = papelera, '[Gmail]/Spam'); una carpeta de usuario es solo su nombre. El destino debe existir en el servidor."
output: "dict. En exito: {status:'ok', uid:int, dest:str, method:'move'|'copy_delete'} donde method indica si se uso UID MOVE atomico o el fallback COPY+STORE \\Deleted+EXPUNGE. En error (sin lanzar): {status:'error', error:str}, p.ej. si COPY o STORE devuelven un typ distinto de OK."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/infra/imap_move_message.py"
---

## Ejemplo

```python
import sys
sys.path.insert(0, "python/functions")
from infra.imap_connect import imap_connect
from infra.imap_move_message import imap_move_message

# conn ya conectado, autenticado y con INBOX seleccionado (lo produce imap_connect).
conn = imap_connect(...)["conn"]  # firma exacta la define imap_connect

# Mover el mensaje UID 12345 a la papelera de Gmail (nota el prefijo [Gmail]/).
print(imap_move_message(conn, 12345, "[Gmail]/Trash"))
# {'status': 'ok', 'uid': 12345, 'dest': '[Gmail]/Trash', 'method': 'move'}

# En un servidor sin MOVE el resultado seria identico salvo method:
# {'status': 'ok', 'uid': 12345, 'dest': 'Archive', 'method': 'copy_delete'}
```

## Cuando usarla

Cuando quieres reubicar un mensaje entre carpetas sin descargarlo ni reenviarlo:
archivar, clasificar en una etiqueta/carpeta, o enviarlo a la papelera. Es la via
correcta para "borrar de verdad" en Gmail (mover a `[Gmail]/Trash`), donde marcar
`\\Deleted` solo quita la etiqueta de la carpeta actual. La funcion abstrae la
diferencia entre servidores con y sin soporte MOVE, asi que el caller no necesita
saber si el backend implementa RFC 6851. Compone tras `imap_search`/`imap_fetch_message`.

## Gotchas

- **Impura**: cambia estado en el servidor de forma persistente y puede borrar el
  mensaje del origen (en el fallback, tras EXPUNGE).
- **Gmail \\Deleted vs Trash**: en Gmail, marcar `\\Deleted` NO borra el mensaje,
  solo le quita la etiqueta de la carpeta actual. Para borrar de verdad hay que
  mover a `[Gmail]/Trash`. Por eso esta funcion es la herramienta de borrado real
  en Gmail (con `dest_mailbox="[Gmail]/Trash"`).
- **Prefijo [Gmail]/**: las carpetas del sistema de Gmail llevan el prefijo
  `[Gmail]/` (`[Gmail]/Trash`, `[Gmail]/Spam`, `[Gmail]/Drafts`, `[Gmail]/Sent Mail`,
  `[Gmail]/All Mail`). Las carpetas/etiquetas de usuario van por su nombre directo.
- **MOVE vs COPY+EXPUNGE**: MOVE (RFC 6851) es atomico; el fallback COPY+STORE
  \\Deleted+EXPUNGE no lo es — si falla entre pasos, el mensaje puede quedar
  duplicado (en origen y destino) o marcado \\Deleted sin expurgar. El EXPUNGE del
  fallback materializa los borrados pendientes del mailbox origen completo.
- **UID estable, no secuencia**: se usa siempre `conn.uid(...)`. El UID es estable
  dentro del mailbox mientras no cambie la UIDVALIDITY; el numero de secuencia se
  desplaza al borrar mensajes y por eso nunca se usa.
- **Nunca lanza**: cualquier fallo (destino inexistente, conexion caida, respuesta
  no-OK) vuelve como `{status:'error', error:str}`.
