"""Tests para dav_collection_ctag.

Smoke deterministas: monkeypatchean urllib.request.urlopen para capturar el
Request (method PROPFIND, Depth:0, auth) y devolver un multistatus simulado.
Cubren: getctag presente, fallback a getetag cuando no hay getctag, ninguno de
los dos, y el path de error HTTP.
"""

import base64
import sys

import infra.dav_collection_ctag  # noqa: F401

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

_XML_CTAG = (
    '<?xml version="1.0"?>'
    '<ns0:multistatus xmlns:ns0="DAV:" xmlns:ns1="http://calendarserver.org/ns/">'
    "<ns0:response><ns0:href>/enmanuel/calendars/calendar/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 200 OK</ns0:status><ns0:prop>"
    "<ns1:getctag>ctag-abc123</ns1:getctag>"
    '<ns0:getetag>"etag-abc123"</ns0:getetag>'
    "</ns0:prop></ns0:propstat></ns0:response></ns0:multistatus>"
)

_XML_SOLO_ETAG = (
    '<?xml version="1.0"?>'
    '<ns0:multistatus xmlns:ns0="DAV:">'
    "<ns0:response><ns0:href>/enmanuel/contacts/addressbook/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 200 OK</ns0:status><ns0:prop>"
    '<ns0:getetag>"etag-only-999"</ns0:getetag>'
    "</ns0:prop></ns0:propstat></ns0:response></ns0:multistatus>"
)

_XML_VACIO = (
    '<?xml version="1.0"?>'
    '<ns0:multistatus xmlns:ns0="DAV:">'
    "<ns0:response><ns0:href>/enmanuel/contacts/addressbook/</ns0:href>"
    "<ns0:propstat><ns0:status>HTTP/1.1 404 Not Found</ns0:status>"
    "<ns0:prop></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(path="/enmanuel/calendars/calendar/"):
    return dav_collection_ctag(
        "https://dav.example.com", "enmanuel", "secret-pw", path
    )


def test_construye_propfind_depth_0(monkeypatch):
    cap = _capture(monkeypatch, _XML_CTAG)
    _call()
    assert cap["method"] == "PROPFIND"
    assert cap["headers"]["depth"] == "0"


def test_basic_auth_header_correcto(monkeypatch):
    cap = _capture(monkeypatch, _XML_CTAG)
    _call()
    expected = "Basic " + base64.b64encode(b"enmanuel:secret-pw").decode("ascii")
    assert cap["headers"]["authorization"] == expected


def test_devuelve_getctag(monkeypatch):
    _capture(monkeypatch, _XML_CTAG)
    res = _call()
    assert res["status"] == "ok"
    assert res["ctag"] == "ctag-abc123"


def test_fallback_a_getetag(monkeypatch):
    _capture(monkeypatch, _XML_SOLO_ETAG)
    res = _call("/enmanuel/contacts/addressbook/")
    assert res["status"] == "ok"
    assert res["ctag"] == '"etag-only-999"'


def test_sin_ctag_ni_etag_devuelve_error(monkeypatch):
    _capture(monkeypatch, _XML_VACIO)
    res = _call("/enmanuel/contacts/addressbook/")
    assert res["status"] == "error"


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
