"""Abre y autentica una conexion IMAP (SSL por defecto) y selecciona un buzon.

Funcion IMPURA: hace I/O de red. Construye un `imaplib.IMAP4_SSL(host, port)`
(o `imaplib.IMAP4(host, port)` si `use_ssl=False`), hace `login(user, password)`
y `select(mailbox)`, y devuelve el objeto de conexion VIVO dentro del dict de
resultado para que las demas funciones del grupo (`imap_list_mailboxes`,
`imap_search`, `imap_fetch_message`) operen sobre el.

Es la primera pieza de un sistema propio (sin browser/CDP) de lectura de correo
multi-proveedor. La autenticacion es usuario + app-password (16 caracteres en
Gmail, o user+pass del proveedor): NO usa OAuth. Las credenciales NO se
resuelven aqui — las pasa la capa de aplicacion (via `pass`/vault).

NUNCA lanza: devuelve un dict con `status` ("ok"/"error"). En error el campo
`conn` no esta presente; el caller debe comprobar `status` antes de usar `conn`.
"""

import imaplib


def imap_connect(
    host: str,
    port: int = 993,
    user: str = "",
    password: str = "",
    mailbox: str = "INBOX",
    use_ssl: bool = True,
    timeout_s: float = 30.0,
) -> dict:
    """Conecta, autentica y selecciona un buzon IMAP.

    Abre el socket IMAP (SSL por defecto), hace `login` con usuario +
    app-password y `select(mailbox)`. El objeto `imaplib.IMAP4[_SSL]` vivo se
    devuelve dentro del dict para componer el resto de operaciones del grupo.

    Args:
        host: servidor IMAP (ej. ``"imap.gmail.com"``). Vacio -> status error.
        port: puerto IMAP. Default 993 (IMAPS). Para STARTTLS/plano suele ser 143.
        user: direccion de correo / usuario de la cuenta.
        password: app-password (16 chars en Gmail) o contrasena del proveedor.
            NO OAuth. Requiere 2FA activado para emitir app-passwords en Gmail.
        mailbox: buzon a seleccionar tras autenticar. Default ``"INBOX"``.
        use_ssl: True usa ``IMAP4_SSL`` (cifrado de extremo a extremo desde el
            saludo). False usa ``IMAP4`` en claro (solo redes de confianza/test).
        timeout_s: timeout del socket en segundos para conectar y operar.

    Returns:
        Dict de estado. En exito::

            {
                "status": "ok",
                "conn": <imaplib.IMAP4_SSL vivo, autenticado y con mailbox seleccionado>,
                "mailbox": <mailbox>,
                "num_messages": <int>,   # mensajes en el buzon (respuesta de SELECT)
            }

        En fallo (host vacio, auth invalida, red, buzon inexistente)::

            {"status": "error", "error": <str>}
    """
    if not host or not host.strip():
        return {"status": "error", "error": "imap_connect: host vacio"}

    host = host.strip()
    conn = None
    try:
        if use_ssl:
            conn = imaplib.IMAP4_SSL(host, int(port), timeout=float(timeout_s))
        else:
            conn = imaplib.IMAP4(host, int(port), timeout=float(timeout_s))

        # login lanza imaplib.IMAP4.error si las credenciales son invalidas.
        conn.login(user, password)

        typ, data = conn.select(mailbox)
        if typ != "OK":
            # data suele traer el motivo (buzon inexistente, etc.).
            reason = _first_str(data)
            try:
                conn.logout()
            except Exception:
                pass
            return {
                "status": "error",
                "error": f"imap_connect: SELECT {mailbox!r} fallo: {reason}",
            }

        num_messages = _parse_int(data)
        return {
            "status": "ok",
            "conn": conn,
            "mailbox": mailbox,
            "num_messages": num_messages,
        }
    except Exception as exc:  # noqa: BLE001 — contrato: nunca lanzar.
        if conn is not None:
            try:
                conn.logout()
            except Exception:
                pass
        return {"status": "error", "error": f"imap_connect: {exc}"}


def _first_str(data) -> str:
    """Devuelve el primer elemento de una respuesta imaplib como str legible."""
    if not data:
        return ""
    item = data[0]
    if isinstance(item, bytes):
        return item.decode("utf-8", errors="replace")
    return str(item)


def _parse_int(data) -> int:
    """Parsea el numero de mensajes de la respuesta de SELECT (lista de bytes)."""
    try:
        return int(_first_str(data))
    except (ValueError, TypeError):
        return 0
