"""Tests para whatsapp_send_message.

whatsapp_send_message compone whatsapp_open_chat con tres primitivas CDP
(cdp_eval, cdp_type_chars, cdp_press_key) y requiere un Chrome vivo. Aqui se
mockean las cuatro con monkeypatch sobre el modulo `browser.whatsapp_send_message`
(donde quedan ligados los nombres por el `from browser.X import Y`), de modo que
NO hace falta Chrome.

Las llamadas a cdp_eval que importan distinguen por el contenido de la expresion:
  - 'aria-label' -> verificacion del destinatario (open_first=False).
  - 'innerText' (composer) -> contenido tecleado, comparado contra el texto.
  - 'role="row"' (#main) -> ultima fila renderizada tras enviar (last_row).
  - cualquier otra (focus del composer) -> value inocuo.
"""

import os
import sys

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

import browser.whatsapp_send_message as wsm
from browser.whatsapp_send_message import whatsapp_send_message


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

def _fake_cdp_eval_factory(*, label_value=None, composer_value="", last_row_value=None):
    """Devuelve un fake de cdp_eval que distingue cada expresion por su contenido."""

    def _fake(expr, *, port=9222, target_url_substr=""):
        if "aria-label" in expr:
            return {"ok": True, "value": label_value, "error": "", "target_url": ""}
        if "innerText.replace(/\\n/g" in expr or "innerText.replace(/\n/g" in expr:
            return {"ok": True, "value": composer_value, "error": "", "target_url": ""}
        if 'role=\\"row\\"' in expr or 'role="row"' in expr:
            return {"ok": True, "value": last_row_value, "error": "", "target_url": ""}
        return {"ok": True, "value": None, "error": "", "target_url": ""}

    return _fake


class _Spy:
    """Registra los argumentos de cada llamada y devuelve un valor fijo."""

    def __init__(self, ret=None):
        self.calls = []
        self.ret = ret if ret is not None else {"ok": True}

    def __call__(self, *args, **kwargs):
        self.calls.append((args, kwargs))
        return self.ret


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

def test_golden_envia_mensaje_y_devuelve_last_row(monkeypatch):
    text = "hola desde el registry"
    last = "hola desde el registry 11:40"

    # open_first=True (default): whatsapp_open_chat abre con exito.
    monkeypatch.setattr(wsm, "whatsapp_open_chat",
                        lambda *a, **k: {"opened": True, "name": a[0]})
    # composer devuelve exactamente el texto; #main devuelve la ultima fila.
    monkeypatch.setattr(wsm, "cdp_eval",
                        _fake_cdp_eval_factory(composer_value=text, last_row_value=last))
    type_spy = _Spy(ret={"ok": True})
    press_spy = _Spy(ret={"ok": True})
    monkeypatch.setattr(wsm, "cdp_type_chars", type_spy)
    monkeypatch.setattr(wsm, "cdp_press_key", press_spy)
    monkeypatch.setattr(wsm.time, "sleep", lambda *a, **k: None)

    res = whatsapp_send_message("NOTAS WASAP", text,
                                port=9222, target_url_substr="whatsapp")

    assert res["sent"] is True
    assert res["name"] == "NOTAS WASAP"
    assert res["last_row"] == last
    # Se tecleo el texto y se pulso Enter una vez.
    assert len(type_spy.calls) == 1
    assert type_spy.calls[0][0][0] == text
    assert len(press_spy.calls) == 1
    assert press_spy.calls[0][0][0] == "Enter"


def test_edge_open_fallido_sent_false_reason(monkeypatch):
    # whatsapp_open_chat no abre: aborta sin tocar el composer.
    monkeypatch.setattr(
        wsm, "whatsapp_open_chat",
        lambda *a, **k: {"opened": False, "name": a[0],
                         "reason": "chat no encontrado en la lista (no cargado o nombre inexacto)"},
    )
    type_spy = _Spy(ret={"ok": True})
    press_spy = _Spy(ret={"ok": True})
    monkeypatch.setattr(wsm, "cdp_eval", _fake_cdp_eval_factory())
    monkeypatch.setattr(wsm, "cdp_type_chars", type_spy)
    monkeypatch.setattr(wsm, "cdp_press_key", press_spy)
    monkeypatch.setattr(wsm.time, "sleep", lambda *a, **k: None)

    res = whatsapp_send_message("Contacto Inexistente", "hola",
                                port=9222, target_url_substr="whatsapp")

    assert res["sent"] is False
    assert res["name"] == "Contacto Inexistente"
    assert "no encontrado" in res["reason"]
    # No se intento escribir ni enviar cuando el chat no abrio.
    assert len(type_spy.calls) == 0
    assert len(press_spy.calls) == 0
    assert "last_row" not in res


def test_seguridad_open_first_false_label_no_coincide_aborta_sin_escribir(monkeypatch):
    # open_first=False y el aria-label del composer NO contiene el name -> abort.
    monkeypatch.setattr(wsm, "whatsapp_open_chat",
                        lambda *a, **k: {"opened": True, "name": a[0]})
    monkeypatch.setattr(
        wsm, "cdp_eval",
        _fake_cdp_eval_factory(label_value="Escribir un mensaje para el grupo OTRO CHAT"),
    )
    type_spy = _Spy(ret={"ok": True})
    press_spy = _Spy(ret={"ok": True})
    monkeypatch.setattr(wsm, "cdp_type_chars", type_spy)
    monkeypatch.setattr(wsm, "cdp_press_key", press_spy)
    monkeypatch.setattr(wsm.time, "sleep", lambda *a, **k: None)

    res = whatsapp_send_message("NOTAS WASAP", "hola",
                                port=9222, target_url_substr="whatsapp",
                                open_first=False)

    assert res["sent"] is False
    assert res["name"] == "NOTAS WASAP"
    assert "abortado por seguridad" in res["reason"]
    # SEGURIDAD: no se llamo a cdp_type_chars ni a cdp_press_key.
    assert len(type_spy.calls) == 0
    assert len(press_spy.calls) == 0


def test_mismatch_composer_sent_false_sin_press_enter(monkeypatch):
    # El composer no contiene el texto esperado tras teclear -> no se envia.
    text = "hola desde el registry"
    composer_real = "holaa desde ell registryy"  # texto distinto (Lexical duplicando)

    monkeypatch.setattr(wsm, "whatsapp_open_chat",
                        lambda *a, **k: {"opened": True, "name": a[0]})
    monkeypatch.setattr(wsm, "cdp_eval",
                        _fake_cdp_eval_factory(composer_value=composer_real))
    type_spy = _Spy(ret={"ok": True})
    press_spy = _Spy(ret={"ok": True})
    monkeypatch.setattr(wsm, "cdp_type_chars", type_spy)
    monkeypatch.setattr(wsm, "cdp_press_key", press_spy)
    monkeypatch.setattr(wsm.time, "sleep", lambda *a, **k: None)

    res = whatsapp_send_message("NOTAS WASAP", text,
                                port=9222, target_url_substr="whatsapp")

    assert res["sent"] is False
    assert res["name"] == "NOTAS WASAP"
    assert "no contiene el texto esperado" in res["reason"]
    assert res["composer"] == composer_real
    # Se tecleo pero NO se pulso Enter por el mismatch.
    assert len(type_spy.calls) == 1
    assert len(press_spy.calls) == 0
