"""Tests para cdp_type_chars.

Mockea urllib.request.urlopen (GET /json) y websocket.create_connection
(conexion fake que acumula los mensajes enviados) para validar el transporte
CDP sin un Chrome real.
"""

import io
import json

import cdp_type_chars as mod
from cdp_type_chars import cdp_type_chars


class _FakeResp:
    """Context manager que imita la respuesta de urllib.request.urlopen."""

    def __init__(self, payload):
        self._buf = io.BytesIO(json.dumps(payload).encode())

    def __enter__(self):
        return self

    def __exit__(self, *exc):
        return False

    def read(self):
        return self._buf.read()


class _FakeWS:
    """Conexion WebSocket fake: acumula los mensajes enviados y nunca recibe."""

    def __init__(self):
        self.sent = []
        self.closed = False

    def settimeout(self, _):
        pass

    def send(self, payload):
        self.sent.append(json.loads(payload))

    def recv(self):
        # Socket vacio: simula timeout no bloqueante para drenar.
        raise TimeoutError("empty")

    def close(self):
        self.closed = True


def _patch_targets(monkeypatch, payload):
    monkeypatch.setattr(
        mod.urllib.request, "urlopen", lambda *a, **k: _FakeResp(payload)
    )


def _patch_sleep(monkeypatch):
    # Evita esperas reales por delay_ms.
    monkeypatch.setattr(mod.time, "sleep", lambda _s: None)


_TARGETS = [
    {"type": "page", "url": "https://web.whatsapp.com/",
     "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/AB12"},
]


def test_escribir_ab_envia_4_dispatchkeyevent_con_text_correctos(monkeypatch):
    """Golden: 'ab' produce 4 Input.dispatchKeyEvent con los text correctos."""
    fake_ws = _FakeWS()
    _patch_targets(monkeypatch, _TARGETS)
    _patch_sleep(monkeypatch)
    monkeypatch.setattr(mod.websocket, "create_connection", lambda *a, **k: fake_ws)

    res = cdp_type_chars("ab", target_url_substr="whatsapp")

    assert res["ok"] is True
    assert res["chars_sent"] == 2
    assert res["error"] == ""

    methods = [m["method"] for m in fake_ws.sent]
    assert methods == ["Input.dispatchKeyEvent"] * 4

    types = [m["params"]["type"] for m in fake_ws.sent]
    assert types == ["keyDown", "keyUp", "keyDown", "keyUp"]

    texts = [m["params"]["text"] for m in fake_ws.sent]
    assert texts == ["a", "a", "b", "b"]

    assert fake_ws.closed is True


def test_text_vacio_no_envia_nada_y_devuelve_ok(monkeypatch):
    """Edge: texto vacio -> 0 chars, ok True, sin mensajes enviados."""
    fake_ws = _FakeWS()
    _patch_targets(monkeypatch, _TARGETS)
    _patch_sleep(monkeypatch)
    monkeypatch.setattr(mod.websocket, "create_connection", lambda *a, **k: fake_ws)

    res = cdp_type_chars("", target_url_substr="whatsapp")

    assert res["ok"] is True
    assert res["chars_sent"] == 0
    assert res["error"] == ""
    assert fake_ws.sent == []


def test_fallo_de_create_connection_devuelve_ok_false(monkeypatch):
    """Error: create_connection lanza -> ok False, error poblado."""
    _patch_targets(monkeypatch, _TARGETS)
    _patch_sleep(monkeypatch)

    def _boom(*a, **k):
        raise ConnectionRefusedError("connection refused")

    monkeypatch.setattr(mod.websocket, "create_connection", _boom)

    res = cdp_type_chars("hola", target_url_substr="whatsapp")

    assert res["ok"] is False
    assert res["chars_sent"] == 0
    assert "connection refused" in res["error"]
