"""Tests para dav_list_calendars.

Smoke deterministas: monkeypatchean urllib.request.urlopen para devolver un
multistatus simulado al estilo Xandikos (PROPFIND Depth:1 sobre un calendar-home
con el propio home plano + dos calendarios CalDAV, uno con color y otro sin) y
una coleccion no-calendario que debe quedar fuera. Cubren: filtrado por
resourcetype caldav:calendar, exclusion del home plano, extraccion de nombre y
color, color ausente -> None, props marcadas 404 ignoradas, y los paths de
error HTTP / sin red.
"""

import sys

import infra.dav_list_calendars  # noqa: F401

mod = sys.modules["infra.dav_list_calendars"]
dav_list_calendars = mod.dav_list_calendars

# Multistatus estilo Xandikos:
#   - /enmanuel/calendars/        -> home plano (collection, sin caldav:calendar)
#   - /enmanuel/calendars/calendar/ -> calendario CalDAV sin color (color en 404)
#   - /enmanuel/calendars/trabajo/  -> calendario CalDAV con calendar-color
#   - /enmanuel/calendars/inbox/    -> coleccion NO-calendario (debe excluirse)
_XML_HOME = (
    '<?xml version="1.0"?>'
    '<ns0:multistatus xmlns:ns0="DAV:" '
    'xmlns:ns1="http://apple.com/ns/ical/" '
    'xmlns:ns2="urn:ietf:params:xml:ns:caldav">'
    # Home plano: collection, sin <ns2:calendar/>.
    "<ns0:response><ns0:href>/enmanuel/calendars/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 200 OK</ns0:status><ns0:prop>"
    "<ns0:displayname>calendars</ns0:displayname>"
    "<ns0:resourcetype><ns0:collection /></ns0:resourcetype>"
    "</ns0:prop></ns0:propstat>"
    "<ns0:propstat><ns0:status>HTTP/1.1 404 Not Found</ns0:status>"
    "<ns0:prop><ns1:calendar-color /></ns0:prop></ns0:propstat>"
    "</ns0:response>"
    # Calendario sin color: el color va en un propstat 404 (no debe leerse).
    "<ns0:response><ns0:href>/enmanuel/calendars/calendar/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 200 OK</ns0:status><ns0:prop>"
    "<ns0:displayname>calendar</ns0:displayname>"
    "<ns0:resourcetype><ns0:collection /><ns2:calendar /></ns0:resourcetype>"
    "</ns0:prop></ns0:propstat>"
    "<ns0:propstat><ns0:status>HTTP/1.1 404 Not Found</ns0:status>"
    "<ns0:prop><ns1:calendar-color /></ns0:prop></ns0:propstat>"
    "</ns0:response>"
    # Calendario con color (Apple #RRGGBBAA) y displayname con acento.
    "<ns0:response><ns0:href>/enmanuel/calendars/trabajo/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 200 OK</ns0:status><ns0:prop>"
    "<ns0:displayname>Trabajo &amp; ocio</ns0:displayname>"
    "<ns0:resourcetype><ns0:collection /><ns2:calendar /></ns0:resourcetype>"
    "<ns1:calendar-color>#FF2968FF</ns1:calendar-color>"
    "</ns0:prop></ns0:propstat></ns0:response>"
    # Coleccion NO-calendario (p.ej. un inbox de scheduling): excluida.
    "<ns0:response><ns0:href>/enmanuel/calendars/inbox/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 200 OK</ns0:status><ns0:prop>"
    "<ns0:displayname>inbox</ns0:displayname>"
    "<ns0:resourcetype><ns0:collection /><ns2:schedule-inbox /></ns0:resourcetype>"
    "</ns0:prop></ns0:propstat></ns0:response>"
    "</ns0:multistatus>"
)


class _FakeResp:
    def __init__(self, payload: str):
        self._payload = payload
        self.status = 207

    def __enter__(self):
        return self

    def __exit__(self, *a):
        return False

    def read(self):
        return self._payload.encode("utf-8")


def _capture(monkeypatch, payload: str):
    captured = {}

    def fake_urlopen(req, timeout=None, context=None):
        captured["method"] = req.get_method()
        captured["headers"] = {k.lower(): v for k, v in req.header_items()}
        return _FakeResp(payload)

    monkeypatch.setattr(mod.urllib.request, "urlopen", fake_urlopen)
    return captured


def _call(home="/enmanuel/calendars/"):
    return dav_list_calendars(
        "https://dav.example.com", "enmanuel", "secret-pw", home
    )


def test_construye_propfind_depth_1(monkeypatch):
    cap = _capture(monkeypatch, _XML_HOME)
    _call()
    assert cap["method"] == "PROPFIND"
    assert cap["headers"]["depth"] == "1"


def test_lista_solo_calendarios(monkeypatch):
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    assert res["status"] == "ok"
    hrefs = [c["href"] for c in res["calendars"]]
    # calendar + trabajo (los dos caldav:calendar), nada mas.
    assert "/enmanuel/calendars/calendar/" in hrefs
    assert "/enmanuel/calendars/trabajo/" in hrefs
    assert len(res["calendars"]) == 2


def test_excluye_home_plano(monkeypatch):
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    hrefs = [c["href"] for c in res["calendars"]]
    assert "/enmanuel/calendars/" not in hrefs


def test_excluye_coleccion_no_calendario(monkeypatch):
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    hrefs = [c["href"] for c in res["calendars"]]
    assert "/enmanuel/calendars/inbox/" not in hrefs


def test_extrae_nombre_y_color(monkeypatch):
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    by_href = {c["href"]: c for c in res["calendars"]}
    trabajo = by_href["/enmanuel/calendars/trabajo/"]
    assert trabajo["name"] == "Trabajo & ocio"  # entidad XML des-escapada
    assert trabajo["color"] == "#FF2968FF"


def test_color_ausente_es_none(monkeypatch):
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    by_href = {c["href"]: c for c in res["calendars"]}
    cal = by_href["/enmanuel/calendars/calendar/"]
    assert cal["name"] == "calendar"
    assert cal["color"] is None


def test_ignora_props_404(monkeypatch):
    """El <calendar-color/> vacio de un propstat 404 NO se lee como color real."""
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    by_href = {c["href"]: c for c in res["calendars"]}
    # calendar tiene calendar-color SOLO en el propstat 404 -> color None, no "".
    assert by_href["/enmanuel/calendars/calendar/"]["color"] is None


def test_ordenado_por_nombre(monkeypatch):
    _capture(monkeypatch, _XML_HOME)
    res = _call()
    names = [c["name"] for c in res["calendars"]]
    assert names == sorted(names, key=str.lower)


def test_httperror_devuelve_status_error(monkeypatch):
    def fake_urlopen(req, timeout=None, context=None):
        raise mod.urllib.error.HTTPError(req.full_url, 401, "Unauthorized", {}, None)

    monkeypatch.setattr(mod.urllib.request, "urlopen", fake_urlopen)
    res = _call()
    assert res["status"] == "error"
    assert res["http_status"] == 401


def test_urlerror_sin_red(monkeypatch):
    def fake_urlopen(req, timeout=None, context=None):
        raise mod.urllib.error.URLError("sin red")

    monkeypatch.setattr(mod.urllib.request, "urlopen", fake_urlopen)
    res = _call()
    assert res["status"] == "error"
    assert res["http_status"] is None
