"""Tests para cdp_eval.

Como cdp_eval requiere un Chrome vivo con remote debugging, se mockean las dos
fronteras de I/O:
  - urllib.request.urlopen  -> devuelve un /json con 2 targets (uno whatsapp).
  - websocket.create_connection -> un fake que responde al id==1 con un value.
"""

import io
import json
import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

import urllib.request

import websocket

from browser.cdp_eval import cdp_eval


# --- Fakes -----------------------------------------------------------------

def _targets_json():
    """Dos targets de tipo page: uno de Google, otro de WhatsApp Web."""
    return [
        {
            "type": "page",
            "url": "https://www.google.com/",
            "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/GOOGLE",
        },
        {
            "type": "page",
            "url": "https://web.whatsapp.com/",
            "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/WA",
        },
    ]


class _FakeHTTPResponse:
    """Context manager que imita la respuesta de urlopen con .read()."""

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

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

    def __enter__(self):
        return self

    def __exit__(self, *exc):
        return False


class _FakeWS:
    """WebSocket fake: guarda el ws_url usado y responde al evaluate con value.

    Antes de la respuesta con id==1, emite un evento intermedio (sin id) para
    verificar que cdp_eval drena eventos hasta encontrar su respuesta.
    """

    last_url = None

    def __init__(self, url, value):
        _FakeWS.last_url = url
        self._value = value
        self._queue = []

    def send(self, raw):
        msg = json.loads(raw)
        if msg.get("id") == 1:
            # Primero un evento de CDP sin id (debe drenarse), luego la respuesta.
            self._queue.append(json.dumps({
                "method": "Runtime.consoleAPICalled",
                "params": {"type": "log"},
            }))
            self._queue.append(json.dumps({
                "id": 1,
                "result": {"result": {"type": "string", "value": self._value}},
            }))

    def recv(self):
        if self._queue:
            return self._queue.pop(0)
        return ""

    def close(self):
        pass


# --- Tests -----------------------------------------------------------------

def test_golden_selecciona_target_por_substr_y_devuelve_value(monkeypatch):
    monkeypatch.setattr(
        urllib.request, "urlopen",
        lambda url, timeout=5: _FakeHTTPResponse(_targets_json()),
    )
    monkeypatch.setattr(
        websocket, "create_connection",
        lambda url, timeout=10.0: _FakeWS(url, "WhatsApp"),
    )

    res = cdp_eval("document.title", port=9222, target_url_substr="whatsapp")

    assert res["ok"] is True
    assert res["value"] == "WhatsApp"
    assert res["error"] == ""
    assert res["target_url"] == "https://web.whatsapp.com/"
    # Confirma que eligio el target whatsapp, no el de google.
    assert _FakeWS.last_url.endswith("/WA")


def test_edge_substr_sin_match_devuelve_ok_false(monkeypatch):
    monkeypatch.setattr(
        urllib.request, "urlopen",
        lambda url, timeout=5: _FakeHTTPResponse(_targets_json()),
    )
    # create_connection no deberia llamarse; si lo hace, revienta el test.
    monkeypatch.setattr(
        websocket, "create_connection",
        lambda *a, **k: (_ for _ in ()).throw(AssertionError("no debe conectar")),
    )

    res = cdp_eval("document.title", port=9222, target_url_substr="nope-no-existe")

    assert res["ok"] is False
    assert res["value"] is None
    assert "no target matching" in res["error"]
    assert "nope-no-existe" in res["error"]
    assert res["target_url"] == ""


def test_error_urlopen_lanza_devuelve_ok_false(monkeypatch):
    def _boom(url, timeout=5):
        raise OSError("connection refused")

    monkeypatch.setattr(urllib.request, "urlopen", _boom)

    res = cdp_eval("document.title", port=9222, target_url_substr="whatsapp")

    assert res["ok"] is False
    assert res["value"] is None
    assert "connection refused" in res["error"]
    assert res["target_url"] == ""
