"""Lista los buzones/carpetas de una cuenta IMAP ya conectada.

Funcion IMPURA: hace I/O de red sobre una conexion `imaplib` viva (la produce
`imap_connect`). Ejecuta `conn.list()`, parsea cada linea de respuesta para
extraer el nombre de la carpeta y lo decodifica de modified-UTF-7 (la
codificacion que IMAP usa en los nombres de buzon, RFC 3501 5.1.3) a Unicode.

NUNCA lanza: devuelve un dict con `status` ("ok"/"error").
"""

import re

# Cada linea de LIST tiene forma:  (\HasNoChildren) "/" "INBOX"
# o sin comillas:                  (\HasNoChildren) "/" INBOX
# Capturamos delimitador y nombre (con o sin comillas).
_LIST_RE = re.compile(rb'^\((?P<flags>[^)]*)\)\s+(?P<delim>"[^"]*"|NIL)\s+(?P<name>.*)$')


def imap_list_mailboxes(conn) -> dict:
    """Lista los nombres de los buzones de la cuenta.

    Ejecuta `conn.list()` y parsea cada entrada, decodificando el nombre de
    modified-UTF-7 a Unicode. Las carpetas anidadas de Gmail aparecen con su
    prefijo (`[Gmail]/Sent Mail`, `[Gmail]/Spam`, ...).

    Args:
        conn: objeto `imaplib.IMAP4[_SSL]` vivo y autenticado (de `imap_connect`).

    Returns:
        Dict de estado. En exito::

            {"status": "ok", "mailboxes": ["INBOX", "[Gmail]/Sent Mail", ...]}

        En fallo (conn invalido, respuesta no OK)::

            {"status": "error", "error": <str>}
    """
    if conn is None:
        return {"status": "error", "error": "imap_list_mailboxes: conn es None"}

    try:
        typ, data = conn.list()
        if typ != "OK":
            return {
                "status": "error",
                "error": f"imap_list_mailboxes: LIST devolvio {typ}",
            }

        mailboxes: list[str] = []
        for line in data:
            name = _extract_name(line)
            if name:
                mailboxes.append(_decode_mailbox(name))
        return {"status": "ok", "mailboxes": mailboxes}
    except Exception as exc:  # noqa: BLE001 — contrato: nunca lanzar.
        return {"status": "error", "error": f"imap_list_mailboxes: {exc}"}


def _extract_name(line) -> bytes:
    """Extrae el nombre crudo (bytes, modified-UTF-7) de una linea de LIST."""
    if line is None:
        return b""
    # Algunos servidores devuelven tuplas (para literales); normalizamos a bytes.
    if isinstance(line, tuple):
        line = b"".join(p if isinstance(p, bytes) else str(p).encode() for p in line)
    if isinstance(line, str):
        line = line.encode("utf-8", errors="replace")

    m = _LIST_RE.match(line.strip())
    if not m:
        return b""
    name = m.group("name").strip()
    # Quita comillas externas si las hay.
    if len(name) >= 2 and name[:1] == b'"' and name[-1:] == b'"':
        name = name[1:-1]
    return name


def _decode_mailbox(raw: bytes) -> str:
    """Decodifica un nombre de buzon de modified-UTF-7 (IMAP) a Unicode.

    IMAP codifica caracteres no-ASCII de los nombres de carpeta en una variante
    de UTF-7 (RFC 3501): `&` introduce la secuencia y `-` la cierra, con `,` en
    lugar de `/` en el base64. Probamos el codec `imap4-utf-7` si el interprete
    lo registra; si no, hacemos la conversion manual; si todo falla, devolvemos
    el nombre como ASCII best-effort para no perder la entrada.
    """
    if not raw:
        return ""
    # 1) Codec nativo si esta disponible (algunos builds lo registran).
    try:
        return raw.decode("imap4-utf-7")
    except (LookupError, UnicodeDecodeError, Exception):  # noqa: BLE001
        pass
    # 2) Conversion manual modified-UTF-7 -> Unicode.
    try:
        return _decode_imap_utf7(raw)
    except Exception:  # noqa: BLE001
        # 3) Fallback crudo: no perdemos la carpeta aunque tenga mojibake.
        return raw.decode("ascii", errors="replace")


def _decode_imap_utf7(raw: bytes) -> str:
    """Implementacion manual de modified-UTF-7 -> str (sin dependencias)."""
    import base64

    s = raw.decode("ascii", errors="replace")
    out: list[str] = []
    i = 0
    n = len(s)
    while i < n:
        ch = s[i]
        if ch == "&":
            j = s.find("-", i + 1)
            if j == -1:
                # & sin cierre: literal mal formado, lo dejamos como esta.
                out.append(s[i:])
                break
            chunk = s[i + 1 : j]
            if chunk == "":
                # "&-" representa un literal "&".
                out.append("&")
            else:
                # En modified-UTF-7 el base64 usa ',' en lugar de '/'.
                b64 = chunk.replace(",", "/")
                pad = "=" * ((4 - len(b64) % 4) % 4)
                decoded = base64.b64decode(b64 + pad)
                out.append(decoded.decode("utf-16-be"))
            i = j + 1
        else:
            out.append(ch)
            i += 1
    return "".join(out)
