"""Lista los recursos de una coleccion DAV via PROPFIND Depth:1.

Funcion impura: hace una peticion HTTP PROPFIND. Construye el header
`Authorization: Basic base64(user:pass)` a mano con stdlib. Devuelve los hrefs
(y getetag cuando el servidor los expone) de los recursos de la coleccion,
parseados del XML multistatus con regex simple (sin dependencias de parser XML
externas). Sirve tanto para colecciones CardDAV como CalDAV. Maneja errores sin
lanzar. Solo usa stdlib (urllib, base64, re, ssl).
"""

import base64
import re
import ssl
import urllib.error
import urllib.request

_HREF_RE = re.compile(r"<(?:[A-Za-z0-9]+:)?href>\s*(.*?)\s*</(?:[A-Za-z0-9]+:)?href>", re.DOTALL | re.IGNORECASE)
_RESPONSE_RE = re.compile(r"<(?:[A-Za-z0-9]+:)?response>(.*?)</(?:[A-Za-z0-9]+:)?response>", re.DOTALL | re.IGNORECASE)
_ETAG_RE = re.compile(r"<(?:[A-Za-z0-9]+:)?getetag>\s*(.*?)\s*</(?:[A-Za-z0-9]+:)?getetag>", re.DOTALL | re.IGNORECASE)

_PROPFIND_BODY = (
    '<?xml version="1.0" encoding="utf-8" ?>'
    '<D:propfind xmlns:D="DAV:"><D:prop>'
    "<D:getetag/><D:resourcetype/>"
    "</D:prop></D:propfind>"
)


def _basic_auth_header(username: str, password: str) -> str:
    raw = ("%s:%s" % (username, password)).encode("utf-8")
    return "Basic " + base64.b64encode(raw).decode("ascii")


def _join_url(base_url: str, collection_path: str) -> str:
    return base_url.rstrip("/") + "/" + collection_path.strip("/") + "/"


def dav_list_resources(
    base_url: str,
    username: str,
    password: str,
    collection_path: str,
    *,
    timeout_s: float = 20.0,
    verify_tls: bool = True,
) -> dict:
    """Lista los recursos de una coleccion DAV (PROPFIND Depth:1).

    Args:
        base_url: URL base del servidor DAV.
        username: usuario para HTTP Basic auth.
        password: contrasena para HTTP Basic auth.
        collection_path: ruta de la coleccion (CardDAV o CalDAV).
        timeout_s: timeout de la peticion en segundos. Default 20.0.
        verify_tls: si True (default) verifica el certificado TLS.

    Returns:
        dict. En exito: {status:'ok', http_status:int,
        resources:[{href:str, etag:str|None}, ...]}. El primer <response> suele
        ser la propia coleccion; se excluye comparando su href con la ruta de la
        coleccion. En error (sin lanzar): {status:'error', error:str,
        http_status:int|None}.
    """
    url = _join_url(base_url, collection_path)
    headers = {
        "Authorization": _basic_auth_header(username, password),
        "Content-Type": "application/xml; charset=utf-8",
        "Depth": "1",
    }
    req = urllib.request.Request(
        url, data=_PROPFIND_BODY.encode("utf-8"), method="PROPFIND", headers=headers
    )

    context = None if verify_tls else ssl._create_unverified_context()
    try:
        with urllib.request.urlopen(req, timeout=timeout_s, context=context) as resp:
            status = resp.status
            xml = resp.read().decode("utf-8", "replace")
    except urllib.error.HTTPError as e:
        return {"status": "error", "error": "http %s" % e.code, "http_status": e.code}
    except urllib.error.URLError as e:
        return {"status": "error", "error": str(e.reason), "http_status": None}
    except Exception as e:  # noqa: BLE001
        return {"status": "error", "error": str(e), "http_status": None}

    coll_tail = collection_path.strip("/").rsplit("/", 1)[-1]
    resources = []
    for block in _RESPONSE_RE.findall(xml):
        href_m = _HREF_RE.search(block)
        if not href_m:
            continue
        href = href_m.group(1).strip()
        # El ultimo segmento del href identifica el recurso. Si coincide con el
        # ultimo segmento de la coleccion, ese <response> ES la coleccion: skip.
        tail = href.rstrip("/").rsplit("/", 1)[-1]
        if tail == coll_tail:
            continue
        etag_m = _ETAG_RE.search(block)
        etag = etag_m.group(1).strip() if etag_m else None
        resources.append({"href": href, "etag": etag})
    return {"status": "ok", "http_status": status, "resources": resources}
