"""Tests para cdp_set_file_input — mockean urlopen + create_connection.

Mockean la capa de red de CDP: urllib.request.urlopen (lista de targets) y
websocket.create_connection (un fake que responde a cada comando DOM por su id, con un
nodeId de raiz, un nodeId de input y una respuesta vacia para setFileInputFiles). Asi NO
hace falta Chrome. Para la validacion de existencia de archivo se usan rutas reales
(__file__) y rutas inexistentes.
"""

import json
import os
import sys
from contextlib import contextmanager

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

from browser import cdp_set_file_input as mod  # noqa: E402
from browser.cdp_set_file_input import cdp_set_file_input  # noqa: E402

_THIS = os.path.abspath(__file__)


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

    def __enter__(self):
        return self

    def __exit__(self, *exc):
        return False

    def read(self):
        return json.dumps(self._payload).encode()


class _FakeWS:
    """WebSocket falso que responde a cada comando DOM por method."""

    def __init__(self, *, query_node_id=4242, set_error=None):
        self.sent = []
        self._inbox = []
        self.closed = False
        self.query_node_id = query_node_id
        self.set_error = set_error

    def send(self, raw):
        msg = json.loads(raw)
        self.sent.append(msg)
        mid = msg["id"]
        method = msg["method"]
        if method == "DOM.getDocument":
            resp = {"id": mid, "result": {"root": {"nodeId": 1}}}
        elif method == "DOM.querySelector":
            resp = {"id": mid, "result": {"nodeId": self.query_node_id}}
        elif method == "DOM.setFileInputFiles":
            if self.set_error is not None:
                resp = {"id": mid, "error": self.set_error}
            else:
                resp = {"id": mid, "result": {}}
        else:  # DOM.enable y demas
            resp = {"id": mid, "result": {}}
        self._inbox.append(json.dumps(resp))

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

    def close(self):
        self.closed = True


@contextmanager
def _patch(targets, ws_obj=None, create_conn_exc=None):
    orig_urlopen = mod.urllib.request.urlopen
    orig_create = mod.websocket.create_connection

    def fake_urlopen(url, timeout=5):
        return _FakeResp(targets)

    def fake_create(ws_url, timeout=10):
        if create_conn_exc is not None:
            raise create_conn_exc
        return ws_obj

    mod.urllib.request.urlopen = fake_urlopen
    mod.websocket.create_connection = fake_create
    try:
        yield
    finally:
        mod.urllib.request.urlopen = orig_urlopen
        mod.websocket.create_connection = orig_create


_TARGETS = [
    {"type": "page", "url": "https://web.whatsapp.com/", "webSocketDebuggerUrl": "ws://x/1"},
]


def test_golden_asigna_archivos_al_input():
    """setFileInputFiles recibe la ruta ABSOLUTA y el nodeId del input localizado."""
    ws = _FakeWS(query_node_id=777)
    with _patch(_TARGETS, ws_obj=ws):
        res = cdp_set_file_input('input[type="file"]', _THIS, target_url_substr="whatsapp")

    assert res["ok"] is True
    assert res["error"] == ""
    assert res["node_id"] == 777
    assert res["files"] == [_THIS]
    # El ultimo comando es setFileInputFiles con la ruta absoluta y el nodeId del input.
    setcmd = [m for m in ws.sent if m["method"] == "DOM.setFileInputFiles"][0]
    assert setcmd["params"]["files"] == [_THIS]
    assert setcmd["params"]["nodeId"] == 777
    assert ws.closed is True


def test_edge_archivo_inexistente_ok_false_sin_red():
    """Una ruta inexistente devuelve ok=False antes de abrir cualquier conexion."""
    res = cdp_set_file_input('input[type="file"]', "/no/existe/imagen.png",
                             target_url_substr="whatsapp")
    assert res["ok"] is False
    assert "not found" in res["error"]
    assert res["node_id"] == 0


def test_edge_selector_no_encontrado_ok_false():
    """Si querySelector devuelve nodeId 0, no se asignan archivos y ok=False."""
    ws = _FakeWS(query_node_id=0)
    with _patch(_TARGETS, ws_obj=ws):
        res = cdp_set_file_input('input[type="file"]', _THIS, target_url_substr="whatsapp")
    assert res["ok"] is False
    assert "no element matches selector" in res["error"]
    # NO se llamo a setFileInputFiles.
    assert all(m["method"] != "DOM.setFileInputFiles" for m in ws.sent)


def test_error_create_connection_lanza_ok_false():
    """Si create_connection lanza, se captura y devuelve ok=False sin relanzar."""
    with _patch(_TARGETS, create_conn_exc=ConnectionRefusedError("ws down")):
        res = cdp_set_file_input('input[type="file"]', _THIS, target_url_substr="whatsapp")
    assert res["ok"] is False
    assert "ws down" in res["error"]
    assert res["files"] == [_THIS]
